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