40d454e94030f5a72b64fada39858beee3f33119
[reactos.git] / reactos / subsys / csrss / api / conio.c
1 /* $Id: conio.c,v 1.24 2001/08/14 22:00:21 ea Exp $
2 *
3 * reactos/subsys/csrss/api/conio.c
4 *
5 * Console I/O functions
6 *
7 * ReactOS Operating System
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ddk/ntddk.h>
13
14 #include <csrss/csrss.h>
15 #include "api.h"
16 #include <ntdll/rtl.h>
17 #include <ddk/ntddblue.h>
18 #include <debug.h>
19
20 #define LOCK RtlEnterCriticalSection(&ActiveConsoleLock)
21 #define UNLOCK RtlLeaveCriticalSection(&ActiveConsoleLock)
22
23
24 /* GLOBALS *******************************************************************/
25
26 static HANDLE ConsoleDeviceHandle;
27 static HANDLE KeyboardDeviceHandle;
28 static PCSRSS_CONSOLE ActiveConsole;
29 CRITICAL_SECTION ActiveConsoleLock;
30 static COORD PhysicalConsoleSize;
31
32 /* FUNCTIONS *****************************************************************/
33
34 CSR_API(CsrAllocConsole)
35 {
36 PCSRSS_CONSOLE Console;
37 HANDLE Process;
38 NTSTATUS Status;
39 CLIENT_ID ClientId;
40
41 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
42 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
43 sizeof(LPC_MESSAGE_HEADER);
44 if( ProcessData->Console )
45 {
46 Reply->Status = STATUS_INVALID_PARAMETER;
47 return STATUS_INVALID_PARAMETER;
48 }
49 Reply->Status = STATUS_SUCCESS;
50 Console = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_CONSOLE ) );
51 if( Console == 0 )
52 {
53 Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
54 return STATUS_INSUFFICIENT_RESOURCES;
55 }
56 Reply->Status = CsrInitConsole( Console );
57 if( !NT_SUCCESS( Reply->Status ) )
58 {
59 RtlFreeHeap( CsrssApiHeap, 0, Console );
60 return Reply->Status;
61 }
62 ProcessData->Console = Console;
63 /* add a reference count because the process is tied to the console */
64 Console->Header.ReferenceCount++;
65 Status = CsrInsertObject( ProcessData, &Reply->Data.AllocConsoleReply.InputHandle, &Console->Header );
66 if( !NT_SUCCESS( Status ) )
67 {
68 CsrDeleteConsole( Console );
69 ProcessData->Console = 0;
70 return Reply->Status = Status;
71 }
72 Status = CsrInsertObject( ProcessData, &Reply->Data.AllocConsoleReply.OutputHandle, &Console->ActiveBuffer->Header );
73 if( !NT_SUCCESS( Status ) )
74 {
75 Console->Header.ReferenceCount--;
76 CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
77 ProcessData->Console = 0;
78 return Reply->Status = Status;
79 }
80 ClientId.UniqueProcess = (HANDLE)ProcessData->ProcessId;
81 Status = NtOpenProcess( &Process, PROCESS_DUP_HANDLE, 0, &ClientId );
82 if( !NT_SUCCESS( Status ) )
83 {
84 DbgPrint( "CSR: NtOpenProcess() failed for handle duplication\n" );
85 Console->Header.ReferenceCount--;
86 ProcessData->Console = 0;
87 CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.OutputHandle );
88 CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
89 Reply->Status = Status;
90 return Status;
91 }
92 Status = NtDuplicateObject( NtCurrentProcess(), &ProcessData->Console->ActiveEvent, Process, &ProcessData->ConsoleEvent, SYNCHRONIZE, FALSE, 0 );
93 if( !NT_SUCCESS( Status ) )
94 {
95 DbgPrint( "CSR: NtDuplicateObject() failed: %x\n", Status );
96 NtClose( Process );
97 Console->Header.ReferenceCount--;
98 CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.OutputHandle );
99 CsrReleaseObject( ProcessData, Reply->Data.AllocConsoleReply.InputHandle );
100 ProcessData->Console = 0;
101 Reply->Status = Status;
102 return Status;
103 }
104 NtClose( Process );
105 return STATUS_SUCCESS;
106 }
107
108 CSR_API(CsrFreeConsole)
109 {
110 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
111 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
112 sizeof(LPC_MESSAGE_HEADER);
113
114 Reply->Status = STATUS_NOT_IMPLEMENTED;
115
116 return(STATUS_NOT_IMPLEMENTED);
117 }
118
119 CSR_API(CsrReadConsole)
120 {
121 ConsoleInput *Input;
122 PCHAR Buffer;
123 int i = 0;
124 ULONG nNumberOfCharsToRead;
125 PCSRSS_CONSOLE Console;
126 NTSTATUS Status;
127
128 /* truncate length to CSRSS_MAX_READ_CONSOLE_REQUEST */
129 nNumberOfCharsToRead = Request->Data.ReadConsoleRequest.NrCharactersToRead > CSRSS_MAX_READ_CONSOLE_REQUEST ? CSRSS_MAX_READ_CONSOLE_REQUEST : Request->Data.ReadConsoleRequest.NrCharactersToRead;
130 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
131 Reply->Header.DataSize = Reply->Header.MessageSize -
132 sizeof(LPC_MESSAGE_HEADER);
133 Buffer = Reply->Data.ReadConsoleReply.Buffer;
134 Reply->Data.ReadConsoleReply.EventHandle = ProcessData->ConsoleEvent;
135 LOCK;
136 Status = CsrGetObject( ProcessData, Request->Data.ReadConsoleRequest.ConsoleHandle, (Object_t **)&Console );
137 if( !NT_SUCCESS( Status ) )
138 {
139 Reply->Status = Status;
140 UNLOCK;
141 return Status;
142 }
143 if( Console->Header.Type != CSRSS_CONSOLE_MAGIC )
144 {
145 Reply->Status = STATUS_INVALID_HANDLE;
146 UNLOCK;
147 return STATUS_INVALID_HANDLE;
148 }
149 for (; i<nNumberOfCharsToRead && Console->InputEvents.Flink != &Console->InputEvents; i++ )
150 {
151 // remove input event from queue
152 Input = (ConsoleInput *)Console->InputEvents.Flink;
153
154 Input->ListEntry.Blink->Flink = Input->ListEntry.Flink;
155 Input->ListEntry.Flink->Blink = Input->ListEntry.Blink;
156 // only pay attention to valid ascii chars, on key down
157 if( Input->InputEvent.EventType == KEY_EVENT &&
158 Input->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
159 Input->InputEvent.Event.KeyEvent.uChar.AsciiChar )
160 {
161 // backspace handling
162 if( Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' )
163 {
164 // echo if it has not already been done, and either we or the client has chars to be deleted
165 if( !Input->Echoed && ( i || Request->Data.ReadConsoleRequest.nCharsCanBeDeleted ) )
166 CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
167 if( i )
168 i-=2; // if we already have something to return, just back it up by 2
169 else
170 { // otherwise, return STATUS_NOTIFY_CLEANUP to tell client to back up its buffer
171 Reply->Data.ReadConsoleReply.NrCharactersRead = 0;
172 Reply->Status = STATUS_NOTIFY_CLEANUP;
173 Console->WaitingChars--;
174 RtlFreeHeap( CsrssApiHeap, 0, Input );
175 UNLOCK;
176 return STATUS_NOTIFY_CLEANUP;
177 }
178 Request->Data.ReadConsoleRequest.nCharsCanBeDeleted--;
179 Input->Echoed = TRUE; // mark as echoed so we don't echo it below
180 }
181 // do not copy backspace to buffer
182 else Buffer[i] = Input->InputEvent.Event.KeyEvent.uChar.AsciiChar;
183 // echo to screen if enabled and we did not already echo the char
184 if( Console->Mode & ENABLE_ECHO_INPUT &&
185 !Input->Echoed &&
186 Input->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
187 CsrpWriteConsole( Console->ActiveBuffer, &Input->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
188 }
189 else i--;
190 Console->WaitingChars--;
191 RtlFreeHeap( CsrssApiHeap, 0, Input );
192 }
193 Reply->Data.ReadConsoleReply.NrCharactersRead = i;
194 if( !i )
195 Reply->Status = STATUS_PENDING; // we didn't read anything
196 else if( Console->Mode & ENABLE_LINE_INPUT )
197 if( !Console->WaitingLines || Buffer[i-1] != '\n' )
198 {
199 Reply->Status = STATUS_PENDING; // line buffered, didn't get a complete line
200 }
201 else {
202 Console->WaitingLines--;
203 Reply->Status = STATUS_SUCCESS; // line buffered, did get a complete line
204 }
205 else Reply->Status = STATUS_SUCCESS; // not line buffered, did read something
206 if( Reply->Status == STATUS_PENDING )
207 {
208 Console->EchoCount = nNumberOfCharsToRead - i;
209 }
210 else {
211 Console->EchoCount = 0; // if the client is no longer waiting on input, do not echo
212 }
213 Reply->Header.MessageSize += i;
214 UNLOCK;
215 return Reply->Status;
216 }
217
218 #define SET_CELL_BUFFER(b,o,c,a)\
219 (b)->Buffer[(o)++]=(c);\
220 (b)->Buffer[(o)++]=(a);
221
222 static VOID FASTCALL
223 ClearLineBuffer (
224 PCSRSS_SCREEN_BUFFER Buff,
225 DWORD StartX
226 )
227 {
228 DWORD Offset = 2 * ((Buff->CurrentY * Buff->MaxX) + StartX);
229
230 for ( ; StartX < Buff->MaxX; StartX ++ )
231 {
232 /* Fill the cell: Offset is incremented by the macro */
233 SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib)
234 }
235 }
236
237 NTSTATUS STDCALL CsrpWriteConsole( PCSRSS_SCREEN_BUFFER Buff, CHAR *Buffer, DWORD Length, BOOL Attrib )
238 {
239 IO_STATUS_BLOCK Iosb;
240 NTSTATUS Status;
241 int i;
242 DWORD Offset;
243
244 for( i = 0; i < Length; i++ )
245 {
246 switch( Buffer[ i ] )
247 {
248 /* --- LF --- */
249 case '\n':
250 Buff->CurrentX = 0;
251 /* slide the viewable screen */
252 if( ((PhysicalConsoleSize.Y + Buff->ShowY) % Buff->MaxY) == (Buff->CurrentY + 1) % Buff->MaxY)
253 if( ++Buff->ShowY == (Buff->MaxY - 1) )
254 Buff->ShowY = 0;
255 if( ++Buff->CurrentY == Buff->MaxY )
256 {
257 Buff->CurrentY = 0;
258 }
259 ClearLineBuffer (Buff, 0);
260 break;
261 /* --- BS --- */
262 case '\b':
263 if( Buff->CurrentX == 0 )
264 {
265 /* slide viewable screen up */
266 if( Buff->ShowY == Buff->CurrentY )
267 {
268 if( Buff->ShowY == 0 )
269 Buff->ShowY = Buff->MaxY;
270 else
271 Buff->ShowY--;
272 }
273 /* slide virtual position up */
274 Buff->CurrentX = Buff->MaxX;
275 if( Buff->CurrentY == 0 )
276 Buff->CurrentY = Buff->MaxY;
277 else
278 Buff->CurrentY--;
279 }
280 else
281 Buff->CurrentX--;
282 Offset = 2 * ((Buff->CurrentY * Buff->MaxX) + Buff->CurrentX);
283 SET_CELL_BUFFER(Buff,Offset,' ',Buff->DefaultAttrib);
284 break;
285 /* --- CR --- */
286 case '\r':
287 Buff->CurrentX = 0;
288 break;
289 /* --- TAB --- */
290 case '\t':
291 CsrpWriteConsole(Buff, " ", (8 - (Buff->CurrentX % 8)), FALSE);
292 break;
293 /* --- */
294 default:
295 Offset = 2 * (((Buff->CurrentY * Buff->MaxX)) + Buff->CurrentX);
296 Buff->Buffer[Offset ++] = Buffer[ i ];
297 if( Attrib )
298 Buff->Buffer[Offset] = Buff->DefaultAttrib;
299 Buff->CurrentX++;
300 if( Buff->CurrentX == Buff->MaxX )
301 {
302 /* if end of line, go to next */
303 Buff->CurrentX = 0;
304 if( ++Buff->CurrentY == Buff->MaxY )
305 {
306 /* if end of buffer, wrap back to beginning */
307 Buff->CurrentY = 0;
308 }
309 /* clear new line */
310 ClearLineBuffer (Buff, 0);
311 /* slide the viewable screen */
312 if( (Buff->CurrentY - Buff->ShowY) == PhysicalConsoleSize.Y )
313 if( ++Buff->ShowY == Buff->MaxY )
314 Buff->ShowY = 0;
315 }
316 }
317 }
318 if( Buff == ActiveConsole->ActiveBuffer )
319 { /* only write to screen if Console is Active, and not scrolled up */
320 if( Attrib )
321 {
322 Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, Buffer, Length, NULL, 0);
323 if (!NT_SUCCESS(Status))
324 DbgPrint("CSR: Write failed\n");
325 }
326 }
327 return(STATUS_SUCCESS);
328 }
329
330 CSR_API(CsrWriteConsole)
331 {
332 BYTE *Buffer = Request->Data.WriteConsoleRequest.Buffer;
333 PCSRSS_SCREEN_BUFFER Buff;
334
335 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
336 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
337 sizeof(LPC_MESSAGE_HEADER);
338
339 LOCK;
340 if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
341 {
342 UNLOCK;
343 return Reply->Status = STATUS_INVALID_HANDLE;
344 }
345 CsrpWriteConsole( Buff, Buffer, Request->Data.WriteConsoleRequest.NrCharactersToWrite, TRUE );
346 UNLOCK;
347 return Reply->Status = STATUS_SUCCESS;
348 }
349
350
351 NTSTATUS STDCALL CsrInitConsoleScreenBuffer( PCSRSS_SCREEN_BUFFER Console )
352 {
353 Console->Header.Type = CSRSS_SCREEN_BUFFER_MAGIC;
354 Console->Header.ReferenceCount = 0;
355 Console->MaxX = PhysicalConsoleSize.X;
356 Console->MaxY = PhysicalConsoleSize.Y * 2;
357 Console->ShowX = 0;
358 Console->ShowY = 0;
359 Console->CurrentX = 0;
360 Console->CurrentY = 0;
361 Console->Buffer = RtlAllocateHeap( CsrssApiHeap, 0, Console->MaxX * Console->MaxY * 2 );
362 if( Console->Buffer == 0 )
363 return STATUS_INSUFFICIENT_RESOURCES;
364 Console->DefaultAttrib = 0x17;
365 /* initialize buffer to be empty with default attributes */
366 for( ; Console->CurrentY < Console->MaxY; Console->CurrentY++ )
367 {
368 ClearLineBuffer (Console, 0);
369 }
370 Console->CursorInfo.bVisible = TRUE;
371 Console->CursorInfo.dwSize = 5;
372 Console->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
373 Console->CurrentX = 0;
374 Console->CurrentY = 0;
375 return STATUS_SUCCESS;
376 }
377
378 VOID STDCALL CsrDeleteScreenBuffer( PCSRSS_SCREEN_BUFFER Buffer )
379 {
380 RtlFreeHeap( CsrssApiHeap, 0, Buffer->Buffer );
381 RtlFreeHeap( CsrssApiHeap, 0, Buffer );
382 }
383
384 NTSTATUS STDCALL CsrInitConsole(PCSRSS_CONSOLE Console)
385 {
386 NTSTATUS Status;
387
388 Console->Title.MaximumLength = Console->Title.Length = 0;
389 Console->Title.Buffer = 0;
390
391 RtlCreateUnicodeString( &Console->Title, L"Command Prompt" );
392
393 Console->Header.ReferenceCount = 0;
394 Console->WaitingChars = 0;
395 Console->WaitingLines = 0;
396 Console->EchoCount = 0;
397 Console->Header.Type = CSRSS_CONSOLE_MAGIC;
398 Console->Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT;
399 Console->InputEvents.Flink = Console->InputEvents.Blink = &Console->InputEvents;
400 Status = NtCreateEvent( &Console->ActiveEvent, STANDARD_RIGHTS_ALL, 0, FALSE, FALSE );
401 if( !NT_SUCCESS( Status ) )
402 {
403 return Status;
404 }
405 Console->ActiveBuffer = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
406 if( !Console->ActiveBuffer )
407 {
408 NtClose( Console->ActiveEvent );
409 return STATUS_INSUFFICIENT_RESOURCES;
410 }
411 Status = CsrInitConsoleScreenBuffer( Console->ActiveBuffer );
412 if( !NT_SUCCESS( Status ) )
413 {
414 NtClose( Console->ActiveEvent );
415 RtlFreeHeap( CsrssApiHeap, 0, Console->ActiveBuffer );
416 return Status;
417 }
418 /* add a reference count because the buffer is tied to the console */
419 Console->ActiveBuffer->Header.ReferenceCount++;
420 /* make console active, and insert into console list */
421 LOCK;
422 if( ActiveConsole )
423 {
424 Console->Prev = ActiveConsole;
425 Console->Next = ActiveConsole->Next;
426 ActiveConsole->Next->Prev = Console;
427 ActiveConsole->Next = Console;
428 }
429 else {
430 Console->Prev = Console;
431 Console->Next = Console;
432 }
433 ActiveConsole = Console;
434 /* copy buffer contents to screen */
435 CsrDrawConsole( Console->ActiveBuffer );
436 UNLOCK;
437 return STATUS_SUCCESS;
438 }
439
440 /***************************************************************
441 * CsrDrawConsole blasts the console buffer onto the screen *
442 * must be called while holding the active console lock *
443 **************************************************************/
444 VOID STDCALL CsrDrawConsole( PCSRSS_SCREEN_BUFFER Buff )
445 {
446 IO_STATUS_BLOCK Iosb;
447 NTSTATUS Status;
448 CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
449 CONSOLE_MODE Mode;
450 int i, y;
451
452 /* first set position to 0,0 */
453 ScrInfo.dwCursorPosition.X = 0;
454 ScrInfo.dwCursorPosition.Y = 0;
455 ScrInfo.wAttributes = Buff->DefaultAttrib;
456 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
457 if( !NT_SUCCESS( Status ) )
458 {
459 DbgPrint( "CSR: Failed to set console info\n" );
460 return;
461 }
462 Mode.dwMode = 0; /* clear ENABLE_PROCESSED_OUTPUT mode */
463 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_MODE, &Mode, sizeof( Mode ), 0, 0 );
464 if( !NT_SUCCESS( Status ) )
465 {
466 DbgPrint( "CSR: Failed to set console mode\n" );
467 return;
468 }
469 /* blast out buffer */
470 for( i = 0, y = Buff->ShowY; i < PhysicalConsoleSize.Y; i++ )
471 {
472 Status = NtWriteFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, &Buff->Buffer[ (Buff->ShowX * 2) + (y * Buff->MaxX * 2) ], PhysicalConsoleSize.X * 2, 0, 0 );
473 if( !NT_SUCCESS( Status ) )
474 {
475 DbgPrint( "CSR: Write to console failed\n" );
476 return;
477 }
478 /* wrap back around the end of the buffer */
479 if( ++y == Buff->MaxY )
480 y = 0;
481 }
482 Mode.dwMode = ENABLE_PROCESSED_OUTPUT;
483 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_MODE, &Mode, sizeof( Mode ), 0, 0 );
484 if( !NT_SUCCESS( Status ) )
485 {
486 DbgPrint( "CSR: Failed to set console mode\n" );
487 return;
488 }
489 ScrInfo.dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
490 ScrInfo.dwCursorPosition.Y = ((Buff->CurrentY + Buff->MaxY) - Buff->ShowY) % Buff->MaxY;
491 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
492 if( !NT_SUCCESS( Status ) )
493 {
494 DbgPrint( "CSR: Failed to set console info\n" );
495 return;
496 }
497 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Buff->CursorInfo, sizeof( Buff->CursorInfo ), 0, 0 );
498 if( !NT_SUCCESS( Status ) )
499 {
500 DbgPrint( "CSR: Failed to set cursor info\n" );
501 return;
502 }
503 }
504
505
506 VOID STDCALL CsrDeleteConsole( PCSRSS_CONSOLE Console )
507 {
508 ConsoleInput *Event;
509 DPRINT1( "CsrDeleteConsole\n" );
510 LOCK;
511 /* Drain input event queue */
512 while( Console->InputEvents.Flink != &Console->InputEvents )
513 {
514 Event = (ConsoleInput *)Console->InputEvents.Flink;
515 Console->InputEvents.Flink = Console->InputEvents.Flink->Flink;
516 Console->InputEvents.Flink->Flink->Blink = &Console->InputEvents;
517 RtlFreeHeap( CsrssApiHeap, 0, Event );
518 }
519 /* Switch to next console */
520 if( ActiveConsole == Console )
521 {
522 if( Console->Next != Console )
523 {
524 ActiveConsole = Console->Next;
525 Console->Prev->Next = Console->Next;
526 Console->Next->Prev = Console->Prev;
527 }
528 else ActiveConsole = 0;
529 }
530 if( ActiveConsole )
531 CsrDrawConsole( ActiveConsole->ActiveBuffer );
532 UNLOCK;
533 if( !--Console->ActiveBuffer->Header.ReferenceCount )
534 CsrDeleteScreenBuffer( Console->ActiveBuffer );
535 NtClose( Console->ActiveEvent );
536 RtlFreeUnicodeString( &Console->Title );
537 RtlFreeHeap( CsrssApiHeap, 0, Console );
538 }
539
540 VOID STDCALL CsrInitConsoleSupport(VOID)
541 {
542 OBJECT_ATTRIBUTES ObjectAttributes;
543 UNICODE_STRING DeviceName;
544 NTSTATUS Status;
545 IO_STATUS_BLOCK Iosb;
546 CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
547
548 DbgPrint("CSR: CsrInitConsoleSupport()\n");
549
550 RtlInitUnicodeString(&DeviceName, L"\\??\\BlueScreen");
551 InitializeObjectAttributes(&ObjectAttributes,
552 &DeviceName,
553 0,
554 NULL,
555 NULL);
556 Status = NtOpenFile(&ConsoleDeviceHandle,
557 FILE_ALL_ACCESS,
558 &ObjectAttributes,
559 &Iosb,
560 0,
561 FILE_SYNCHRONOUS_IO_ALERT);
562 if (!NT_SUCCESS(Status))
563 {
564 DbgPrint("CSR: Failed to open console. Expect problems.\n");
565 }
566 // DbgPrint("CSR: ConsoleDeviceHandle %x\n", ConsoleDeviceHandle);
567
568 RtlInitUnicodeString(&DeviceName, L"\\??\\Keyboard");
569 InitializeObjectAttributes(&ObjectAttributes,
570 &DeviceName,
571 0,
572 NULL,
573 NULL);
574 Status = NtOpenFile(&KeyboardDeviceHandle,
575 FILE_ALL_ACCESS,
576 &ObjectAttributes,
577 &Iosb,
578 0,
579 0);
580 if (!NT_SUCCESS(Status))
581 {
582 DbgPrint("CSR: Failed to open keyboard. Expect problems.\n");
583 }
584
585 ActiveConsole = 0;
586 RtlInitializeCriticalSection( &ActiveConsoleLock );
587 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, 0, 0, &ScrInfo, sizeof( ScrInfo ) );
588 if( !NT_SUCCESS( Status ) )
589 {
590 DbgPrint( "CSR: Failed to get console info, expect trouble\n" );
591 return;
592 }
593 PhysicalConsoleSize = ScrInfo.dwSize;
594 }
595
596 VOID Console_Api( DWORD RefreshEvent )
597 {
598 /* keep reading events from the keyboard and stuffing them into the current
599 console's input queue */
600 ConsoleInput *KeyEventRecord;
601 ConsoleInput *TempInput;
602 IO_STATUS_BLOCK Iosb;
603 NTSTATUS Status;
604 HANDLE Events[2]; // 0 = keyboard, 1 = refresh
605 int c;
606 int updown;
607 PCSRSS_CONSOLE SwapConsole = 0; // console we are thinking about swapping with
608
609 Events[0] = 0;
610 Status = NtCreateEvent( &Events[0], STANDARD_RIGHTS_ALL, NULL, FALSE, FALSE );
611 if( !NT_SUCCESS( Status ) )
612 {
613 DbgPrint( "CSR: NtCreateEvent failed: %x\n", Status );
614 NtTerminateProcess( NtCurrentProcess(), Status );
615 }
616 Events[1] = (HANDLE)RefreshEvent;
617 while( 1 )
618 {
619 KeyEventRecord = RtlAllocateHeap(CsrssApiHeap,
620 0,
621 sizeof(ConsoleInput));
622 if ( KeyEventRecord == 0 )
623 {
624 DbgPrint( "CSR: Memory allocation failure!" );
625 continue;
626 }
627 KeyEventRecord->InputEvent.EventType = KEY_EVENT;
628 Status = NtReadFile( KeyboardDeviceHandle, Events[0], NULL, NULL, &Iosb, &KeyEventRecord->InputEvent.Event.KeyEvent, sizeof( KEY_EVENT_RECORD ), NULL, 0 );
629 if( !NT_SUCCESS( Status ) )
630 {
631 DbgPrint( "CSR: ReadFile on keyboard device failed\n" );
632 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
633 continue;
634 }
635 if( Status == STATUS_PENDING )
636 {
637 while( 1 )
638 {
639 Status = NtWaitForMultipleObjects( 2, Events, WaitAny, FALSE, NULL );
640 if( Status == STATUS_WAIT_0 + 1 )
641 {
642 LOCK;
643 CsrDrawConsole( ActiveConsole->ActiveBuffer );
644 UNLOCK;
645 continue;
646 }
647 else if( Status != STATUS_WAIT_0 )
648 {
649 DbgPrint( "CSR: NtWaitForMultipleObjects failed: %x, exiting\n", Status );
650 NtTerminateProcess( NtCurrentProcess(), Status );
651 }
652 else break;
653 }
654 }
655 if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED )&& KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_TAB )
656 if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
657 {
658 ANSI_STRING Title;
659 void * Buffer;
660 COORD *pos;
661 unsigned int src, dst;
662
663 /* alt-tab, swap consoles */
664 // move SwapConsole to next console, and print its title
665 LOCK;
666 if( !SwapConsole )
667 SwapConsole = ActiveConsole;
668
669 if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED )
670 SwapConsole = SwapConsole->Prev;
671 else SwapConsole = SwapConsole->Next;
672 Title.MaximumLength = RtlUnicodeStringToAnsiSize( &SwapConsole->Title );
673 Title.Length = 0;
674 Buffer = RtlAllocateHeap( CsrssApiHeap,
675 0,
676 sizeof( COORD ) + Title.MaximumLength );
677 pos = (COORD *)Buffer;
678 Title.Buffer = Buffer + sizeof( COORD );
679
680 /* this does not seem to work
681 RtlUnicodeStringToAnsiString( &Title, &SwapConsole->Title, FALSE ); */
682 // temp hack
683 for( src = 0, dst = 0; src < SwapConsole->Title.Length; src++, dst++ )
684 Title.Buffer[dst] = (char)SwapConsole->Title.Buffer[dst];
685
686 pos->Y = PhysicalConsoleSize.Y / 2;
687 pos->X = ( PhysicalConsoleSize.X - Title.MaximumLength ) / 2;
688 // redraw the console to clear off old title
689 CsrDrawConsole( ActiveConsole->ActiveBuffer );
690 Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
691 NULL,
692 NULL,
693 NULL,
694 &Iosb,
695 IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
696 0,
697 0,
698 Buffer,
699 sizeof (COORD) + Title.MaximumLength );
700 if( !NT_SUCCESS( Status ) )
701 {
702 DPRINT1( "Error writing to console\n" );
703 }
704 RtlFreeHeap( CsrssApiHeap, 0, Buffer );
705
706 UNLOCK;
707 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
708 continue;
709 }
710 else {
711 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
712 continue;
713 }
714 else if( SwapConsole &&
715 KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_MENU &&
716 KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == FALSE )
717 {
718 // alt key released, swap consoles
719 PCSRSS_CONSOLE tmp;
720
721 LOCK;
722 if( SwapConsole != ActiveConsole )
723 {
724 // first remove swapconsole from the list
725 SwapConsole->Prev->Next = SwapConsole->Next;
726 SwapConsole->Next->Prev = SwapConsole->Prev;
727 // now insert before activeconsole
728 SwapConsole->Next = ActiveConsole;
729 SwapConsole->Prev = ActiveConsole->Prev;
730 ActiveConsole->Prev->Next = SwapConsole;
731 ActiveConsole->Prev = SwapConsole;
732 }
733 ActiveConsole = SwapConsole;
734 SwapConsole = 0;
735 CsrDrawConsole( ActiveConsole->ActiveBuffer );
736
737 UNLOCK;
738 }
739 if( KeyEventRecord->InputEvent.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) && (KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP || KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_DOWN) )
740 {
741 if( KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE )
742 {
743 /* scroll up or down */
744 LOCK;
745 if( ActiveConsole == 0 )
746 {
747 DbgPrint( "CSR: No Active Console!\n" );
748 UNLOCK;
749 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
750 continue;
751 }
752 if( KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode == VK_UP )
753 {
754 /* only scroll up if there is room to scroll up into */
755 if( ActiveConsole->ActiveBuffer->ShowY != ((ActiveConsole->ActiveBuffer->CurrentY + 1) % ActiveConsole->ActiveBuffer->MaxY) )
756 ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY + ActiveConsole->ActiveBuffer->MaxY - 1) % ActiveConsole->ActiveBuffer->MaxY;
757 }
758 else if( ActiveConsole->ActiveBuffer->ShowY != ActiveConsole->ActiveBuffer->CurrentY )
759 /* only scroll down if there is room to scroll down into */
760 if( ActiveConsole->ActiveBuffer->ShowY % ActiveConsole->ActiveBuffer->MaxY != ActiveConsole->ActiveBuffer->CurrentY )
761 if( ((ActiveConsole->ActiveBuffer->CurrentY + 1) % ActiveConsole->ActiveBuffer->MaxY) != (ActiveConsole->ActiveBuffer->ShowY + PhysicalConsoleSize.Y) % ActiveConsole->ActiveBuffer->MaxY )
762 ActiveConsole->ActiveBuffer->ShowY = (ActiveConsole->ActiveBuffer->ShowY + 1) % ActiveConsole->ActiveBuffer->MaxY;
763 CsrDrawConsole( ActiveConsole->ActiveBuffer );
764 UNLOCK;
765 }
766 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
767 continue;
768 }
769 LOCK;
770 if( ActiveConsole == 0 )
771 {
772 DbgPrint( "CSR: No Active Console!\n" );
773 UNLOCK;
774 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
775 continue;
776 }
777 // process special keys if enabled
778 if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) )
779 switch( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar )
780 {
781 case '\r':
782 // add a \n to the queue as well
783 // first add the \r
784 updown = KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown;
785 KeyEventRecord->Echoed = FALSE;
786 KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\r';
787 KeyEventRecord->ListEntry.Flink = &ActiveConsole->InputEvents;
788 KeyEventRecord->ListEntry.Blink = ActiveConsole->InputEvents.Blink;
789 ActiveConsole->InputEvents.Blink->Flink = &KeyEventRecord->ListEntry;
790 ActiveConsole->InputEvents.Blink = &KeyEventRecord->ListEntry;
791 ActiveConsole->WaitingChars++;
792 KeyEventRecord = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( ConsoleInput ) );
793 if( !KeyEventRecord )
794 {
795 DbgPrint( "CSR: Failed to allocate KeyEventRecord\n" );
796 UNLOCK;
797 continue;
798 }
799 KeyEventRecord->InputEvent.EventType = KEY_EVENT;
800 KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown = updown;
801 KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualKeyCode = 0;
802 KeyEventRecord->InputEvent.Event.KeyEvent.wVirtualScanCode = 0;
803 KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar = '\n';
804 }
805 // add event to the queue
806 KeyEventRecord->ListEntry.Flink = &ActiveConsole->InputEvents;
807 KeyEventRecord->ListEntry.Blink = ActiveConsole->InputEvents.Blink;
808 ActiveConsole->InputEvents.Blink->Flink = &KeyEventRecord->ListEntry;
809 ActiveConsole->InputEvents.Blink = &KeyEventRecord->ListEntry;
810 // if line input mode is enabled, only wake the client on enter key down
811 if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT ) ||
812 ( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' &&
813 KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE ) )
814 {
815 NtSetEvent( ActiveConsole->ActiveEvent, 0 );
816 if( KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
817 ActiveConsole->WaitingLines++;
818 }
819 KeyEventRecord->Echoed = FALSE;
820 if( ActiveConsole->Mode & (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT) &&
821 KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' &&
822 KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown )
823 {
824 // walk the input queue looking for a char to backspace
825 for( TempInput = (ConsoleInput *)ActiveConsole->InputEvents.Blink;
826 TempInput != (ConsoleInput *)&ActiveConsole->InputEvents &&
827 (TempInput->InputEvent.EventType != KEY_EVENT ||
828 TempInput->InputEvent.Event.KeyEvent.bKeyDown == FALSE ||
829 TempInput->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\b' );
830 TempInput = (ConsoleInput *)TempInput->ListEntry.Blink );
831 // if we found one, delete it, otherwise, wake the client
832 if( TempInput != (ConsoleInput *)&ActiveConsole->InputEvents )
833 {
834 // delete previous key in queue, maybe echo backspace to screen, and do not place backspace on queue
835 TempInput->ListEntry.Blink->Flink = TempInput->ListEntry.Flink;
836 TempInput->ListEntry.Flink->Blink = TempInput->ListEntry.Blink;
837 if( TempInput->Echoed )
838 CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
839 RtlFreeHeap( CsrssApiHeap, 0, TempInput );
840 KeyEventRecord->ListEntry.Blink->Flink = KeyEventRecord->ListEntry.Flink;
841 KeyEventRecord->ListEntry.Flink->Blink = KeyEventRecord->ListEntry.Blink;
842 RtlFreeHeap( CsrssApiHeap, 0, KeyEventRecord );
843 ActiveConsole->WaitingChars -= 2;
844 }
845 else NtSetEvent( ActiveConsole->ActiveEvent, 0 );
846 }
847 else {
848 // echo chars if we are supposed to and client is waiting for some
849 if( ActiveConsole->Mode & ENABLE_ECHO_INPUT && ActiveConsole->EchoCount &&
850 KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar &&
851 KeyEventRecord->InputEvent.Event.KeyEvent.bKeyDown == TRUE &&
852 KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar != '\r' )
853 {
854 // mark the char as already echoed
855 CsrpWriteConsole( ActiveConsole->ActiveBuffer, &KeyEventRecord->InputEvent.Event.KeyEvent.uChar.AsciiChar, 1, TRUE );
856 ActiveConsole->EchoCount--;
857 KeyEventRecord->Echoed = TRUE;
858 }
859 }
860 ActiveConsole->WaitingChars++;
861 if( !(ActiveConsole->Mode & ENABLE_LINE_INPUT) )
862 NtSetEvent( ActiveConsole->ActiveEvent, 0 );
863 UNLOCK;
864 }
865 }
866
867 CSR_API(CsrGetScreenBufferInfo)
868 {
869 NTSTATUS Status;
870 PCSRSS_SCREEN_BUFFER Buff;
871 PCONSOLE_SCREEN_BUFFER_INFO pInfo;
872 IO_STATUS_BLOCK Iosb;
873
874 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
875 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
876 sizeof(LPC_MESSAGE_HEADER);
877
878 LOCK;
879 if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.ScreenBufferInfoRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
880 {
881 UNLOCK;
882 return Reply->Status = STATUS_INVALID_HANDLE;
883 }
884 pInfo = &Reply->Data.ScreenBufferInfoReply.Info;
885 if( Buff == ActiveConsole->ActiveBuffer )
886 {
887 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, 0, 0, pInfo, sizeof( *pInfo ) );
888 if( !NT_SUCCESS( Status ) )
889 DbgPrint( "CSR: Failed to get console info, expect trouble\n" );
890 Reply->Status = Status;
891 }
892 else {
893 pInfo->dwSize.X = PhysicalConsoleSize.X;
894 pInfo->dwSize.Y = PhysicalConsoleSize.Y;
895 pInfo->dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
896 pInfo->dwCursorPosition.Y = (Buff->CurrentY + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
897 pInfo->wAttributes = Buff->DefaultAttrib;
898 pInfo->srWindow.Left = 0;
899 pInfo->srWindow.Right = PhysicalConsoleSize.X - 1;
900 pInfo->srWindow.Top = 0;
901 pInfo->srWindow.Bottom = PhysicalConsoleSize.Y - 1;
902 Reply->Status = STATUS_SUCCESS;
903 }
904 UNLOCK;
905 return Reply->Status;
906 }
907
908 CSR_API(CsrSetCursor)
909 {
910 NTSTATUS Status;
911 PCSRSS_SCREEN_BUFFER Buff;
912 CONSOLE_SCREEN_BUFFER_INFO Info;
913 IO_STATUS_BLOCK Iosb;
914
915 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
916 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
917 sizeof(LPC_MESSAGE_HEADER);
918
919 LOCK;
920 if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.SetCursorRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
921 {
922 UNLOCK;
923 return Reply->Status = STATUS_INVALID_HANDLE;
924 }
925 Info.dwCursorPosition = Request->Data.SetCursorRequest.Position;
926 Info.wAttributes = Buff->DefaultAttrib;
927 if( Buff == ActiveConsole->ActiveBuffer )
928 {
929 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &Info, sizeof( Info ), 0, 0 );
930 if( !NT_SUCCESS( Status ) )
931 DbgPrint( "CSR: Failed to set console info, expect trouble\n" );
932 }
933 Buff->CurrentX = Info.dwCursorPosition.X + Buff->ShowX;
934 Buff->CurrentY = (Info.dwCursorPosition.Y + Buff->ShowY) % Buff->MaxY;
935 UNLOCK;
936 return Reply->Status = Status;
937 }
938
939 CSR_API(CsrWriteConsoleOutputChar)
940 {
941 BYTE *Buffer = Request->Data.WriteConsoleOutputCharRequest.String;
942 PCSRSS_SCREEN_BUFFER Buff;
943 DWORD X, Y;
944 NTSTATUS Status;
945 IO_STATUS_BLOCK Iosb;
946
947 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
948 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
949 sizeof(LPC_MESSAGE_HEADER);
950 LOCK;
951 if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
952 {
953 UNLOCK;
954 return Reply->Status = STATUS_INVALID_HANDLE;
955 }
956 X = Buff->CurrentX;
957 Y = Buff->CurrentY;
958 Buff->CurrentX = Request->Data.WriteConsoleOutputCharRequest.Coord.X;
959 Buff->CurrentY = Request->Data.WriteConsoleOutputCharRequest.Coord.Y;
960 Buffer[Request->Data.WriteConsoleOutputCharRequest.Length] = 0;
961 CsrpWriteConsole( Buff, Buffer, Request->Data.WriteConsoleOutputCharRequest.Length, FALSE );
962 if( ActiveConsole->ActiveBuffer == Buff )
963 {
964 Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
965 NULL,
966 NULL,
967 NULL,
968 &Iosb,
969 IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER,
970 0,
971 0,
972 &Request->Data.WriteConsoleOutputCharRequest.Coord,
973 sizeof (COORD) + Request->Data.WriteConsoleOutputCharRequest.Length );
974 if( !NT_SUCCESS( Status ) )
975 DPRINT1( "Failed to write output chars: %x\n", Status );
976 }
977 Reply->Data.WriteConsoleOutputCharReply.EndCoord.X = Buff->CurrentX - Buff->ShowX;
978 Reply->Data.WriteConsoleOutputCharReply.EndCoord.Y = (Buff->CurrentY + Buff->MaxY - Buff->ShowY) % Buff->MaxY;
979 Buff->CurrentY = Y;
980 Buff->CurrentX = X;
981 UNLOCK;
982 return Reply->Status = STATUS_SUCCESS;
983 }
984
985 CSR_API(CsrFillOutputChar)
986 {
987 PCSRSS_SCREEN_BUFFER Buff;
988 DWORD X, Y, i;
989
990 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
991 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
992 sizeof(LPC_MESSAGE_HEADER);
993
994 LOCK;
995 if( !NT_SUCCESS( CsrGetObject( ProcessData, Request->Data.FillOutputRequest.ConsoleHandle, (Object_t **)&Buff ) ) || Buff->Header.Type != CSRSS_SCREEN_BUFFER_MAGIC )
996 {
997 UNLOCK;
998 return Reply->Status = STATUS_INVALID_HANDLE;
999 }
1000 X = Request->Data.FillOutputRequest.Position.X + Buff->ShowX;
1001 Y = Request->Data.FillOutputRequest.Position.Y + Buff->ShowY;
1002 for( i = 0; i < 20000; i++ );
1003 for( i = 0; i < Request->Data.FillOutputRequest.Length; i++ )
1004 {
1005 Buff->Buffer[ (Y * 2 * Buff->MaxX) + (X * 2) ] = Request->Data.FillOutputRequest.Char;
1006 if( ++X == Buff->MaxX )
1007 {
1008 if( ++Y == Buff->MaxY )
1009 Y = 0;
1010 X = 0;
1011 }
1012 }
1013 if( Buff == ActiveConsole->ActiveBuffer )
1014 CsrDrawConsole( Buff );
1015 UNLOCK;
1016 return Reply->Status;
1017 }
1018
1019 CSR_API(CsrReadInputEvent)
1020 {
1021 PCSRSS_CONSOLE Console;
1022 NTSTATUS Status;
1023 ConsoleInput *Input;
1024
1025 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1026 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1027 sizeof(LPC_MESSAGE_HEADER);
1028 Reply->Data.ReadInputReply.Event = ProcessData->ConsoleEvent;
1029
1030 LOCK;
1031 Status = CsrGetObject( ProcessData, Request->Data.ReadInputRequest.ConsoleHandle, (Object_t **)&Console );
1032 if( !NT_SUCCESS( Status ) || (Status = Console->Header.Type == CSRSS_CONSOLE_MAGIC ? 0 : STATUS_INVALID_HANDLE))
1033 {
1034 Reply->Status = Status;
1035 UNLOCK;
1036 return Status;
1037 }
1038 // only get input if there is input, and we are not in line input mode, or if we are, if we have a whole line
1039 if( Console->InputEvents.Flink != &Console->InputEvents &&
1040 ( !Console->Mode & ENABLE_LINE_INPUT || Console->WaitingLines ) )
1041 {
1042 Input = (ConsoleInput *)Console->InputEvents.Flink;
1043 Input->ListEntry.Blink->Flink = Input->ListEntry.Flink;
1044 Input->ListEntry.Flink->Blink = Input->ListEntry.Blink;
1045 Reply->Data.ReadInputReply.Input = Input->InputEvent;
1046 if( Console->Mode & ENABLE_LINE_INPUT &&
1047 Input->InputEvent.Event.KeyEvent.bKeyDown == FALSE &&
1048 Input->InputEvent.Event.KeyEvent.uChar.AsciiChar == '\n' )
1049 Console->WaitingLines--;
1050 Console->WaitingChars--;
1051 RtlFreeHeap( CsrssApiHeap, 0, Input );
1052 Reply->Data.ReadInputReply.MoreEvents = (Console->InputEvents.Flink != &Console->InputEvents) ? TRUE : FALSE;
1053 Status = STATUS_SUCCESS;
1054 }
1055 else Status = STATUS_PENDING;
1056 UNLOCK;
1057 return Reply->Status = Status;
1058 }
1059
1060 CSR_API(CsrWriteConsoleOutputAttrib)
1061 {
1062 int c;
1063 PCSRSS_SCREEN_BUFFER Buff;
1064 NTSTATUS Status;
1065 int X, Y;
1066 IO_STATUS_BLOCK Iosb;
1067
1068 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1069 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1070 sizeof(LPC_MESSAGE_HEADER);
1071 LOCK;
1072 Status = CsrGetObject( ProcessData, Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1073 if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1074 {
1075 Reply->Status = Status;
1076 UNLOCK;
1077 return Status;
1078 }
1079 X = Buff->CurrentX;
1080 Y = Buff->CurrentY;
1081 Buff->CurrentX = Request->Data.WriteConsoleOutputAttribRequest.Coord.X + Buff->ShowX;
1082 Buff->CurrentY = (Request->Data.WriteConsoleOutputAttribRequest.Coord.Y + Buff->ShowY) % Buff->MaxY;
1083 for( c = 0; c < Request->Data.WriteConsoleOutputAttribRequest.Length; c++ )
1084 {
1085 Buff->Buffer[(Buff->CurrentY * Buff->MaxX * 2) + (Buff->CurrentX * 2) + 1] = Request->Data.WriteConsoleOutputAttribRequest.String[c];
1086 if( ++Buff->CurrentX == Buff->MaxX )
1087 {
1088 Buff->CurrentX = 0;
1089 if( ++Buff->CurrentY == Buff->MaxY )
1090 Buff->CurrentY = 0;
1091 }
1092 }
1093 if( Buff == ActiveConsole->ActiveBuffer )
1094 {
1095 Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
1096 NULL,
1097 NULL,
1098 NULL,
1099 &Iosb,
1100 IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE,
1101 0,
1102 0,
1103 &Request->Data.WriteConsoleOutputAttribRequest.Coord,
1104 Request->Data.WriteConsoleOutputAttribRequest.Length +
1105 sizeof (COORD) );
1106 if( !NT_SUCCESS( Status ) )
1107 DPRINT1( "Failed to write output attributes to console\n" );
1108 }
1109 Reply->Data.WriteConsoleOutputAttribReply.EndCoord.X = Buff->CurrentX - Buff->ShowX;
1110 Reply->Data.WriteConsoleOutputAttribReply.EndCoord.Y = ( Buff->CurrentY + Buff->MaxY - Buff->ShowY ) % Buff->MaxY;
1111 Buff->CurrentX = X;
1112 Buff->CurrentY = Y;
1113 UNLOCK;
1114 return Reply->Status = STATUS_SUCCESS;
1115 }
1116
1117 CSR_API(CsrFillOutputAttrib)
1118 {
1119 int c;
1120 PCSRSS_SCREEN_BUFFER Buff;
1121 NTSTATUS Status;
1122 int X, Y;
1123 IO_STATUS_BLOCK Iosb;
1124 OUTPUT_ATTRIBUTE Attr;
1125
1126 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1127 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1128 sizeof(LPC_MESSAGE_HEADER);
1129 LOCK;
1130 Status = CsrGetObject( ProcessData, Request->Data.FillOutputAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1131 if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1132 {
1133 Reply->Status = Status;
1134 UNLOCK;
1135 return Status;
1136 }
1137 X = Buff->CurrentX;
1138 Y = Buff->CurrentY;
1139 Buff->CurrentX = Request->Data.FillOutputAttribRequest.Coord.X + Buff->ShowX;
1140 Buff->CurrentY = Request->Data.FillOutputAttribRequest.Coord.Y + Buff->ShowY;
1141 for( c = 0; c < Request->Data.FillOutputAttribRequest.Length; c++ )
1142 {
1143 Buff->Buffer[(Buff->CurrentY * Buff->MaxX * 2) + (Buff->CurrentX * 2) + 1] = Request->Data.FillOutputAttribRequest.Attribute;
1144 if( ++Buff->CurrentX == Buff->MaxX )
1145 {
1146 Buff->CurrentX = 0;
1147 if( ++Buff->CurrentY == Buff->MaxY )
1148 Buff->CurrentY = 0;
1149 }
1150 }
1151 if( Buff == ActiveConsole->ActiveBuffer )
1152 {
1153 Attr.wAttribute = Request->Data.FillOutputAttribRequest.Attribute;
1154 Attr.nLength = Request->Data.FillOutputAttribRequest.Length;
1155 Attr.dwCoord = Request->Data.FillOutputAttribRequest.Coord;
1156 Status = NtDeviceIoControlFile( ConsoleDeviceHandle,
1157 NULL,
1158 NULL,
1159 NULL,
1160 &Iosb,
1161 IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE,
1162 &Attr,
1163 sizeof (Attr),
1164 0,
1165 0 );
1166 if( !NT_SUCCESS( Status ) )
1167 DPRINT1( "Failed to fill output attribute\n" );
1168 }
1169 Buff->CurrentX = X;
1170 Buff->CurrentY = Y;
1171 UNLOCK;
1172 return Reply->Status = STATUS_SUCCESS;
1173 }
1174
1175
1176 CSR_API(CsrGetCursorInfo)
1177 {
1178 PCSRSS_SCREEN_BUFFER Buff;
1179 NTSTATUS Status;
1180
1181 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1182 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1183 sizeof(LPC_MESSAGE_HEADER);
1184 LOCK;
1185 Status = CsrGetObject( ProcessData, Request->Data.GetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
1186 if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1187 {
1188 Reply->Status = Status;
1189 UNLOCK;
1190 return Status;
1191 }
1192 Reply->Data.GetCursorInfoReply.Info = Buff->CursorInfo;
1193 UNLOCK;
1194 return Reply->Status = STATUS_SUCCESS;
1195 }
1196
1197 CSR_API(CsrSetCursorInfo)
1198 {
1199 PCSRSS_SCREEN_BUFFER Buff;
1200 NTSTATUS Status;
1201 IO_STATUS_BLOCK Iosb;
1202
1203 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1204 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1205 sizeof(LPC_MESSAGE_HEADER);
1206 LOCK;
1207 Status = CsrGetObject( ProcessData, Request->Data.SetCursorInfoRequest.ConsoleHandle, (Object_t **)&Buff );
1208 if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1209 {
1210 Reply->Status = Status;
1211 UNLOCK;
1212 return Status;
1213 }
1214 Buff->CursorInfo = Request->Data.SetCursorInfoRequest.Info;
1215 if( Buff == ActiveConsole->ActiveBuffer )
1216 {
1217 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_CURSOR_INFO, &Buff->CursorInfo, sizeof( Buff->CursorInfo ), 0, 0 );
1218 if( !NT_SUCCESS( Status ) )
1219 {
1220 DbgPrint( "CSR: Failed to set cursor info\n" );
1221 return Reply->Status = Status;
1222 }
1223 }
1224 UNLOCK;
1225 return Reply->Status = STATUS_SUCCESS;
1226 }
1227
1228 CSR_API(CsrSetTextAttrib)
1229 {
1230 NTSTATUS Status;
1231 CONSOLE_SCREEN_BUFFER_INFO ScrInfo;
1232 IO_STATUS_BLOCK Iosb;
1233 PCSRSS_SCREEN_BUFFER Buff;
1234
1235 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1236 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) -
1237 sizeof(LPC_MESSAGE_HEADER);
1238 LOCK;
1239 Status = CsrGetObject( ProcessData, Request->Data.SetAttribRequest.ConsoleHandle, (Object_t **)&Buff );
1240 if( !NT_SUCCESS( Status ) || (Status = Buff->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC ? 0 : STATUS_INVALID_HANDLE ))
1241 {
1242 Reply->Status = Status;
1243 UNLOCK;
1244 return Status;
1245 }
1246 Buff->DefaultAttrib = Request->Data.SetAttribRequest.Attrib;
1247 if( Buff == ActiveConsole->ActiveBuffer )
1248 {
1249 ScrInfo.wAttributes = Buff->DefaultAttrib;
1250 ScrInfo.dwCursorPosition.X = Buff->CurrentX - Buff->ShowX;
1251 ScrInfo.dwCursorPosition.Y = ((Buff->CurrentY + Buff->MaxY) - Buff->ShowY) % Buff->MaxY;
1252 Status = NtDeviceIoControlFile( ConsoleDeviceHandle, NULL, NULL, NULL, &Iosb, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, &ScrInfo, sizeof( ScrInfo ), 0, 0 );
1253 if( !NT_SUCCESS( Status ) )
1254 {
1255 DbgPrint( "CSR: Failed to set console info\n" );
1256 UNLOCK;
1257 return Reply->Status = Status;
1258 }
1259 }
1260 UNLOCK;
1261 return Reply->Status = STATUS_SUCCESS;
1262 }
1263
1264 CSR_API(CsrSetConsoleMode)
1265 {
1266 NTSTATUS Status;
1267 PCSRSS_CONSOLE Console;
1268 PCSRSS_SCREEN_BUFFER Buff;
1269
1270 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1271 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
1272 LOCK;
1273 Status = CsrGetObject( ProcessData, Request->Data.SetConsoleModeRequest.ConsoleHandle, (Object_t **)&Console );
1274 if( !NT_SUCCESS( Status ) )
1275 {
1276 Reply->Status = Status;
1277 UNLOCK;
1278 return Status;
1279 }
1280 Buff = (PCSRSS_SCREEN_BUFFER)Console;
1281 if( Console->Header.Type == CSRSS_CONSOLE_MAGIC )
1282 Console->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_INPUT_MODE_VALID;
1283 else if( Console->Header.Type == CSRSS_SCREEN_BUFFER_MAGIC )
1284 Buff->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_OUTPUT_MODE_VALID;
1285 else {
1286 Reply->Status = STATUS_INVALID_HANDLE;
1287 UNLOCK;
1288 return Status;
1289 }
1290 UNLOCK;
1291 Reply->Status = STATUS_SUCCESS;
1292 return Reply->Status;
1293 }
1294
1295 CSR_API(CsrGetConsoleMode)
1296 {
1297 NTSTATUS Status;
1298 PCSRSS_CONSOLE Console;
1299 PCSRSS_SCREEN_BUFFER Buff; /* gee, I really wish I could use an anonymous union here */
1300
1301 Reply->Header.MessageSize = sizeof(CSRSS_API_REPLY);
1302 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
1303 LOCK;
1304 Status = CsrGetObject( ProcessData, Request->Data.GetConsoleModeRequest.ConsoleHandle, (Object_t **)&Console );
1305 if( !NT_SUCCESS( Status ) )
1306 {
1307 Reply->Status = Status;
1308 UNLOCK;
1309 return Status;
1310 }
1311 Reply->Status = STATUS_SUCCESS;
1312 Buff = (PCSRSS_SCREEN_BUFFER)Console;
1313 if( Console->Header.Type = CSRSS_CONSOLE_MAGIC )
1314 Reply->Data.GetConsoleModeReply.ConsoleMode = Console->Mode;
1315 else if( Buff->Header.Type = CSRSS_SCREEN_BUFFER_MAGIC )
1316 Reply->Data.GetConsoleModeReply.ConsoleMode = Buff->Mode;
1317 else Status = STATUS_INVALID_HANDLE;
1318 UNLOCK;
1319 return Reply->Status;
1320 }
1321
1322 CSR_API(CsrCreateScreenBuffer)
1323 {
1324 PCSRSS_SCREEN_BUFFER Buff = RtlAllocateHeap( CsrssApiHeap, 0, sizeof( CSRSS_SCREEN_BUFFER ) );
1325 NTSTATUS Status;
1326
1327 Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1328 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
1329 if( !Buff )
1330 Reply->Status = STATUS_INSUFFICIENT_RESOURCES;
1331 LOCK;
1332 Status = CsrInitConsoleScreenBuffer( Buff );
1333 if( !NT_SUCCESS( Status ) )
1334 Reply->Status = Status;
1335 else {
1336 Status = CsrInsertObject( ProcessData, &Reply->Data.CreateScreenBufferReply.OutputHandle, &Buff->Header );
1337 if( !NT_SUCCESS( Status ) )
1338 Reply->Status = Status;
1339 else Reply->Status = STATUS_SUCCESS;
1340 }
1341 UNLOCK;
1342 return Reply->Status;
1343 }
1344
1345 CSR_API(CsrSetScreenBuffer)
1346 {
1347 NTSTATUS Status;
1348 PCSRSS_SCREEN_BUFFER Buff;
1349
1350 Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1351 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
1352 LOCK;
1353 Status = CsrGetObject( ProcessData, Request->Data.SetActiveScreenBufferRequest.OutputHandle, (Object_t **)&Buff );
1354 if( !NT_SUCCESS( Status ) )
1355 Reply->Status = Status;
1356 else {
1357 // drop reference to old buffer, maybe delete
1358 if( !InterlockedDecrement( &ProcessData->Console->ActiveBuffer->Header.ReferenceCount ) )
1359 CsrDeleteScreenBuffer( ProcessData->Console->ActiveBuffer );
1360 // tie console to new buffer
1361 ProcessData->Console->ActiveBuffer = Buff;
1362 // inc ref count on new buffer
1363 InterlockedIncrement( &Buff->Header.ReferenceCount );
1364 // if the console is active, redraw it
1365 if( ActiveConsole == ProcessData->Console )
1366 CsrDrawConsole( Buff );
1367 Reply->Status = STATUS_SUCCESS;
1368 }
1369 UNLOCK;
1370 return Reply->Status;
1371 }
1372
1373 CSR_API(CsrSetTitle)
1374 {
1375 NTSTATUS Status;
1376 PCSRSS_CONSOLE Console;
1377
1378 Reply->Header.MessageSize = sizeof( CSRSS_API_REPLY );
1379 Reply->Header.DataSize = sizeof(CSRSS_API_REPLY) - sizeof(LPC_MESSAGE_HEADER);
1380 LOCK;
1381 Status = CsrGetObject( ProcessData, Request->Data.SetTitleRequest.Console, (Object_t **)&Console );
1382 if( !NT_SUCCESS( Status ) )
1383 Reply->Status = Status;
1384 else {
1385 // copy title to console
1386 RtlFreeUnicodeString( &Console->Title );
1387 RtlCreateUnicodeString( &Console->Title, Request->Data.SetTitleRequest.Title );
1388 Reply->Status = STATUS_SUCCESS;
1389 }
1390 UNLOCK;
1391 return Reply->Status;
1392 }
1393
1394 CSR_API(CsrGetTitle)
1395 {
1396 NTSTATUS Status;
1397 PCSRSS_CONSOLE Console;
1398
1399 Reply->Header.MessageSize = sizeof (CSRSS_API_REPLY);
1400 Reply->Header.DataSize =
1401 sizeof (CSRSS_API_REPLY)
1402 - sizeof(LPC_MESSAGE_HEADER);
1403 LOCK;
1404 Status = CsrGetObject (
1405 ProcessData,
1406 Request->Data.GetTitleRequest.ConsoleHandle,
1407 (Object_t **) & Console
1408 );
1409 if ( !NT_SUCCESS( Status ) )
1410 {
1411 Reply->Status = Status;
1412 }
1413 else
1414 {
1415 HANDLE ConsoleHandle = Request->Data.GetTitleRequest.ConsoleHandle;
1416
1417 /* Copy title of the console to the user title buffer */
1418 RtlZeroMemory (
1419 & Reply->Data.GetTitleReply,
1420 sizeof (CSRSS_GET_TITLE_REPLY)
1421 );
1422 Reply->Data.GetTitleReply.ConsoleHandle = ConsoleHandle;
1423 Reply->Data.GetTitleReply.Length = Console->Title.Length;
1424 wcscpy (Reply->Data.GetTitleReply.Title, Console->Title.Buffer);
1425 Reply->Status = STATUS_SUCCESS;
1426 }
1427 UNLOCK;
1428 return Reply->Status;
1429 }
1430
1431 /* EOF */