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