2 * reactos/subsys/csrss/win32csr/conio.c
4 * Console I/O functions
6 * ReactOS Operating System
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
18 WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
20 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
21 MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
23 /* FUNCTIONS *****************************************************************/
25 CSR_API(CsrReadConsole
)
27 PLIST_ENTRY CurrentEntry
;
32 ULONG nNumberOfCharsToRead
, CharSize
;
33 PCSRSS_CONSOLE Console
;
36 DPRINT("CsrReadConsole\n");
38 CharSize
= (Request
->Data
.ReadConsoleRequest
.Unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
40 nNumberOfCharsToRead
= Request
->Data
.ReadConsoleRequest
.NrCharactersToRead
;
41 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
42 Request
->Header
.u1
.s1
.DataLength
= sizeof(CSR_API_MESSAGE
) - sizeof(PORT_MESSAGE
);
44 Buffer
= (PCHAR
)Request
->Data
.ReadConsoleRequest
.Buffer
;
45 UnicodeBuffer
= (PWCHAR
)Buffer
;
46 if (!Win32CsrValidateBuffer(ProcessData
, Buffer
, nNumberOfCharsToRead
, CharSize
))
47 return STATUS_ACCESS_VIOLATION
;
49 if (Request
->Data
.ReadConsoleRequest
.NrCharactersRead
* sizeof(WCHAR
) > nNumberOfCharsToRead
* CharSize
)
50 return STATUS_INVALID_PARAMETER
;
52 Status
= ConioLockConsole(ProcessData
, Request
->Data
.ReadConsoleRequest
.ConsoleHandle
,
53 &Console
, GENERIC_READ
);
54 if (! NT_SUCCESS(Status
))
58 Request
->Data
.ReadConsoleRequest
.EventHandle
= ProcessData
->ConsoleEvent
;
60 Status
= STATUS_PENDING
; /* we haven't read anything (yet) */
61 if (Console
->Mode
& ENABLE_LINE_INPUT
)
63 if (Console
->LineBuffer
== NULL
)
65 /* Starting a new line */
66 Console
->LineMaxSize
= max(256, nNumberOfCharsToRead
);
67 Console
->LineBuffer
= HeapAlloc(Win32CsrApiHeap
, 0, Console
->LineMaxSize
* sizeof(WCHAR
));
68 if (Console
->LineBuffer
== NULL
)
70 Status
= STATUS_NO_MEMORY
;
73 Console
->LineComplete
= FALSE
;
74 Console
->LineUpPressed
= FALSE
;
75 Console
->LineInsertToggle
= 0;
76 Console
->LineWakeupMask
= Request
->Data
.ReadConsoleRequest
.CtrlWakeupMask
;
77 Console
->LineSize
= Request
->Data
.ReadConsoleRequest
.NrCharactersRead
;
78 Console
->LinePos
= Console
->LineSize
;
79 /* pre-filling the buffer is only allowed in the Unicode API,
80 * so we don't need to worry about conversion */
81 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
82 if (Console
->LineSize
== Console
->LineMaxSize
)
84 Console
->LineComplete
= TRUE
;
89 /* If we don't have a complete line yet, process the pending input */
90 while (!Console
->LineComplete
&& !IsListEmpty(&Console
->InputEvents
))
92 /* remove input event from queue */
93 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
94 if (IsListEmpty(&Console
->InputEvents
))
96 ResetEvent(Console
->ActiveEvent
);
98 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
100 /* only pay attention to key down */
101 if (KEY_EVENT
== Input
->InputEvent
.EventType
102 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
104 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
105 Request
->Data
.ReadConsoleRequest
.ControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
107 HeapFree(Win32CsrApiHeap
, 0, Input
);
110 /* Check if we have a complete line to read from */
111 if (Console
->LineComplete
)
113 while (i
< nNumberOfCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
115 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
116 if (Request
->Data
.ReadConsoleRequest
.Unicode
)
117 UnicodeBuffer
[i
++] = Char
;
119 ConsoleInputUnicodeCharToAnsiChar(Console
, &Buffer
[i
++], &Char
);
121 if (Console
->LinePos
== Console
->LineSize
)
123 /* Entire line has been read */
124 HeapFree(Win32CsrApiHeap
, 0, Console
->LineBuffer
);
125 Console
->LineBuffer
= NULL
;
127 Status
= STATUS_SUCCESS
;
132 /* Character input */
133 while (i
< nNumberOfCharsToRead
&& !IsListEmpty(&Console
->InputEvents
))
135 /* remove input event from queue */
136 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
137 if (IsListEmpty(&Console
->InputEvents
))
139 ResetEvent(Console
->ActiveEvent
);
141 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
143 /* only pay attention to valid ascii chars, on key down */
144 if (KEY_EVENT
== Input
->InputEvent
.EventType
145 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
146 && Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
148 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
149 if (Request
->Data
.ReadConsoleRequest
.Unicode
)
150 UnicodeBuffer
[i
++] = Char
;
152 ConsoleInputUnicodeCharToAnsiChar(Console
, &Buffer
[i
++], &Char
);
153 Status
= STATUS_SUCCESS
; /* did read something */
155 HeapFree(Win32CsrApiHeap
, 0, Input
);
159 Request
->Data
.ReadConsoleRequest
.NrCharactersRead
= i
;
160 ConioUnlockConsole(Console
);
166 ConioInputEventToAnsi(PCSRSS_CONSOLE Console
, PINPUT_RECORD InputEvent
)
168 if (InputEvent
->EventType
== KEY_EVENT
)
170 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
171 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
172 ConsoleInputUnicodeCharToAnsiChar(Console
,
173 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
178 static NTSTATUS FASTCALL
179 ConioProcessChar(PCSRSS_CONSOLE Console
,
180 PINPUT_RECORD InputEvent
)
182 /* Check for pause or unpause */
183 if (InputEvent
->EventType
== KEY_EVENT
&& InputEvent
->Event
.KeyEvent
.bKeyDown
)
185 WORD vk
= InputEvent
->Event
.KeyEvent
.wVirtualKeyCode
;
186 if (!(Console
->PauseFlags
& PAUSED_FROM_KEYBOARD
))
188 DWORD cks
= InputEvent
->Event
.KeyEvent
.dwControlKeyState
;
189 if (Console
->Mode
& ENABLE_LINE_INPUT
&&
190 (vk
== VK_PAUSE
|| (vk
== 'S' &&
191 (cks
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) &&
192 !(cks
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
)))))
194 ConioPause(Console
, PAUSED_FROM_KEYBOARD
);
195 return STATUS_SUCCESS
;
200 if ((vk
< VK_SHIFT
|| vk
> VK_CAPITAL
) && vk
!= VK_LWIN
&&
201 vk
!= VK_RWIN
&& vk
!= VK_NUMLOCK
&& vk
!= VK_SCROLL
)
203 ConioUnpause(Console
, PAUSED_FROM_KEYBOARD
);
204 return STATUS_SUCCESS
;
209 /* add event to the queue */
210 ConsoleInput
*ConInRec
= RtlAllocateHeap(Win32CsrApiHeap
, 0, sizeof(ConsoleInput
));
211 if (ConInRec
== NULL
)
212 return STATUS_INSUFFICIENT_RESOURCES
;
213 ConInRec
->InputEvent
= *InputEvent
;
214 InsertTailList(&Console
->InputEvents
, &ConInRec
->ListEntry
);
215 SetEvent(Console
->ActiveEvent
);
216 return STATUS_SUCCESS
;
219 static DWORD FASTCALL
220 ConioGetShiftState(PBYTE KeyState
)
224 if (KeyState
[VK_CAPITAL
] & 1)
225 ssOut
|= CAPSLOCK_ON
;
227 if (KeyState
[VK_NUMLOCK
] & 1)
230 if (KeyState
[VK_SCROLL
] & 1)
231 ssOut
|= SCROLLLOCK_ON
;
233 if (KeyState
[VK_SHIFT
] & 0x80)
234 ssOut
|= SHIFT_PRESSED
;
236 if (KeyState
[VK_LCONTROL
] & 0x80)
237 ssOut
|= LEFT_CTRL_PRESSED
;
238 if (KeyState
[VK_RCONTROL
] & 0x80)
239 ssOut
|= RIGHT_CTRL_PRESSED
;
241 if (KeyState
[VK_LMENU
] & 0x80)
242 ssOut
|= LEFT_ALT_PRESSED
;
243 if (KeyState
[VK_RMENU
] & 0x80)
244 ssOut
|= RIGHT_ALT_PRESSED
;
250 ConioProcessKey(MSG
*msg
, PCSRSS_CONSOLE Console
, BOOL TextMode
)
252 static BYTE KeyState
[256] = { 0 };
253 /* MSDN mentions that you should use the last virtual key code received
254 * when putting a virtual key identity to a WM_CHAR message since multiple
255 * or translated keys may be involved. */
256 static UINT LastVirtualKey
= 0;
261 UINT VirtualScanCode
;
264 BOOLEAN Fake
; // synthesized, not a real event
265 BOOLEAN NotChar
; // message should not be used to return a character
268 VirtualScanCode
= (msg
->lParam
>> 16) & 0xff;
269 Down
= msg
->message
== WM_KEYDOWN
|| msg
->message
== WM_CHAR
||
270 msg
->message
== WM_SYSKEYDOWN
|| msg
->message
== WM_SYSCHAR
;
272 GetKeyboardState(KeyState
);
273 ShiftState
= ConioGetShiftState(KeyState
);
275 if (msg
->message
== WM_CHAR
|| msg
->message
== WM_SYSCHAR
)
277 VirtualKeyCode
= LastVirtualKey
;
278 UnicodeChar
= msg
->wParam
;
285 VirtualKeyCode
= msg
->wParam
;
286 RetChars
= ToUnicodeEx(VirtualKeyCode
,
293 UnicodeChar
= (1 == RetChars
? Chars
[0] : 0);
296 er
.EventType
= KEY_EVENT
;
297 er
.Event
.KeyEvent
.bKeyDown
= Down
;
298 er
.Event
.KeyEvent
.wRepeatCount
= RepeatCount
;
299 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= UnicodeChar
;
300 er
.Event
.KeyEvent
.dwControlKeyState
= ShiftState
;
301 er
.Event
.KeyEvent
.wVirtualKeyCode
= VirtualKeyCode
;
302 er
.Event
.KeyEvent
.wVirtualScanCode
= VirtualScanCode
;
306 if (0 != (ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
))
307 && VK_TAB
== VirtualKeyCode
)
311 TuiSwapConsole(ShiftState
& SHIFT_PRESSED
? -1 : 1);
316 else if (VK_MENU
== VirtualKeyCode
&& ! Down
)
318 if (TuiSwapConsole(0))
327 DPRINT1("No Active Console!\n");
331 Fake
= UnicodeChar
&&
332 (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
&&
333 msg
->message
!= WM_KEYUP
&& msg
->message
!= WM_SYSKEYUP
);
334 NotChar
= (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
);
336 LastVirtualKey
= msg
->wParam
;
338 DPRINT ("csrss: %s %s %s %s %02x %02x '%lc' %04x\n",
339 Down
? "down" : "up ",
340 (msg
->message
== WM_CHAR
|| msg
->message
== WM_SYSCHAR
) ?
342 Fake
? "fake" : "real",
343 NotChar
? "notc" : "char",
346 (UnicodeChar
>= L
' ') ? UnicodeChar
: L
'.',
352 /* process Ctrl-C and Ctrl-Break */
353 if (Console
->Mode
& ENABLE_PROCESSED_INPUT
&&
354 er
.Event
.KeyEvent
.bKeyDown
&&
355 ((er
.Event
.KeyEvent
.wVirtualKeyCode
== VK_PAUSE
) ||
356 (er
.Event
.KeyEvent
.wVirtualKeyCode
== 'C')) &&
357 (er
.Event
.KeyEvent
.dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)))
359 PCSRSS_PROCESS_DATA current
;
360 PLIST_ENTRY current_entry
;
361 DPRINT1("Console_Api Ctrl-C\n");
362 current_entry
= Console
->ProcessList
.Flink
;
363 while (current_entry
!= &Console
->ProcessList
)
365 current
= CONTAINING_RECORD(current_entry
, CSRSS_PROCESS_DATA
, ProcessEntry
);
366 current_entry
= current_entry
->Flink
;
367 ConioConsoleCtrlEvent((DWORD
)CTRL_C_EVENT
, current
);
369 if (Console
->LineBuffer
&& !Console
->LineComplete
)
371 /* Line input is in progress; end it */
372 Console
->LinePos
= Console
->LineSize
= 0;
373 Console
->LineComplete
= TRUE
;
378 if (0 != (er
.Event
.KeyEvent
.dwControlKeyState
379 & (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
))
380 && (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
381 || VK_DOWN
== er
.Event
.KeyEvent
.wVirtualKeyCode
))
383 if (er
.Event
.KeyEvent
.bKeyDown
)
385 /* scroll up or down */
386 if (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
)
388 /* only scroll up if there is room to scroll up into */
389 if (Console
->ActiveBuffer
->CurrentY
!= Console
->ActiveBuffer
->MaxY
- 1)
391 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+
392 Console
->ActiveBuffer
->MaxY
- 1) %
393 Console
->ActiveBuffer
->MaxY
;
394 Console
->ActiveBuffer
->CurrentY
++;
399 /* only scroll down if there is room to scroll down into */
400 if (Console
->ActiveBuffer
->CurrentY
!= 0)
402 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+ 1) %
403 Console
->ActiveBuffer
->MaxY
;
404 Console
->ActiveBuffer
->CurrentY
--;
407 ConioDrawConsole(Console
);
411 ConioProcessChar(Console
, &er
);
414 CSR_API(CsrReadInputEvent
)
416 PLIST_ENTRY CurrentEntry
;
417 PCSRSS_CONSOLE Console
;
419 BOOLEAN Done
= FALSE
;
422 DPRINT("CsrReadInputEvent\n");
424 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
425 Request
->Header
.u1
.s1
.DataLength
= sizeof(CSR_API_MESSAGE
) - sizeof(PORT_MESSAGE
);
426 Request
->Data
.ReadInputRequest
.Event
= ProcessData
->ConsoleEvent
;
428 Status
= ConioLockConsole(ProcessData
, Request
->Data
.ReadInputRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
429 if (! NT_SUCCESS(Status
))
434 /* only get input if there is any */
435 CurrentEntry
= Console
->InputEvents
.Flink
;
436 while (CurrentEntry
!= &Console
->InputEvents
)
438 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
439 CurrentEntry
= CurrentEntry
->Flink
;
443 Request
->Data
.ReadInputRequest
.MoreEvents
= TRUE
;
447 RemoveEntryList(&Input
->ListEntry
);
451 Request
->Data
.ReadInputRequest
.Input
= Input
->InputEvent
;
452 if (Request
->Data
.ReadInputRequest
.Unicode
== FALSE
)
454 ConioInputEventToAnsi(Console
, &Request
->Data
.ReadInputRequest
.Input
);
459 HeapFree(Win32CsrApiHeap
, 0, Input
);
463 Status
= STATUS_SUCCESS
;
465 Status
= STATUS_PENDING
;
467 if (IsListEmpty(&Console
->InputEvents
))
469 ResetEvent(Console
->ActiveEvent
);
472 ConioUnlockConsole(Console
);
477 CSR_API(CsrFlushInputBuffer
)
479 PLIST_ENTRY CurrentEntry
;
480 PCSRSS_CONSOLE Console
;
484 DPRINT("CsrFlushInputBuffer\n");
486 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
487 Request
->Header
.u1
.s1
.DataLength
= sizeof(CSR_API_MESSAGE
) - sizeof(PORT_MESSAGE
);
488 Status
= ConioLockConsole(ProcessData
,
489 Request
->Data
.FlushInputBufferRequest
.ConsoleInput
,
492 if(! NT_SUCCESS(Status
))
497 /* Discard all entries in the input event queue */
498 while (!IsListEmpty(&Console
->InputEvents
))
500 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
501 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
502 /* Destroy the event */
503 HeapFree(Win32CsrApiHeap
, 0, Input
);
505 ResetEvent(Console
->ActiveEvent
);
507 ConioUnlockConsole(Console
);
509 return STATUS_SUCCESS
;
512 CSR_API(CsrGetNumberOfConsoleInputEvents
)
515 PCSRSS_CONSOLE Console
;
516 PLIST_ENTRY CurrentItem
;
519 DPRINT("CsrGetNumberOfConsoleInputEvents\n");
521 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
522 Request
->Header
.u1
.s1
.DataLength
= Request
->Header
.u1
.s1
.TotalLength
- sizeof(PORT_MESSAGE
);
524 Status
= ConioLockConsole(ProcessData
, Request
->Data
.GetNumInputEventsRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
525 if (! NT_SUCCESS(Status
))
530 CurrentItem
= Console
->InputEvents
.Flink
;
533 /* If there are any events ... */
534 while (CurrentItem
!= &Console
->InputEvents
)
536 CurrentItem
= CurrentItem
->Flink
;
540 ConioUnlockConsole(Console
);
542 Request
->Data
.GetNumInputEventsRequest
.NumInputEvents
= NumEvents
;
544 return STATUS_SUCCESS
;
547 CSR_API(CsrPeekConsoleInput
)
550 PCSRSS_CONSOLE Console
;
552 PLIST_ENTRY CurrentItem
;
553 PINPUT_RECORD InputRecord
;
557 DPRINT("CsrPeekConsoleInput\n");
559 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
560 Request
->Header
.u1
.s1
.DataLength
= sizeof(CSR_API_MESSAGE
) - sizeof(PORT_MESSAGE
);
562 Status
= ConioLockConsole(ProcessData
, Request
->Data
.GetNumInputEventsRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
563 if(! NT_SUCCESS(Status
))
568 InputRecord
= Request
->Data
.PeekConsoleInputRequest
.InputRecord
;
569 Length
= Request
->Data
.PeekConsoleInputRequest
.Length
;
571 if (!Win32CsrValidateBuffer(ProcessData
, InputRecord
, Length
, sizeof(INPUT_RECORD
)))
573 ConioUnlockConsole(Console
);
574 return STATUS_ACCESS_VIOLATION
;
579 if (! IsListEmpty(&Console
->InputEvents
))
581 CurrentItem
= Console
->InputEvents
.Flink
;
583 while (CurrentItem
!= &Console
->InputEvents
&& NumItems
< Length
)
585 Item
= CONTAINING_RECORD(CurrentItem
, ConsoleInput
, ListEntry
);
588 *InputRecord
= Item
->InputEvent
;
590 if (Request
->Data
.PeekConsoleInputRequest
.Unicode
== FALSE
)
592 ConioInputEventToAnsi(Console
, InputRecord
);
596 CurrentItem
= CurrentItem
->Flink
;
600 ConioUnlockConsole(Console
);
602 Request
->Data
.PeekConsoleInputRequest
.Length
= NumItems
;
604 return STATUS_SUCCESS
;
607 CSR_API(CsrWriteConsoleInput
)
609 PINPUT_RECORD InputRecord
;
610 PCSRSS_CONSOLE Console
;
615 DPRINT("CsrWriteConsoleInput\n");
617 Request
->Header
.u1
.s1
.TotalLength
= sizeof(CSR_API_MESSAGE
);
618 Request
->Header
.u1
.s1
.DataLength
= sizeof(CSR_API_MESSAGE
) - sizeof(PORT_MESSAGE
);
620 Status
= ConioLockConsole(ProcessData
, Request
->Data
.WriteConsoleInputRequest
.ConsoleHandle
, &Console
, GENERIC_WRITE
);
621 if (! NT_SUCCESS(Status
))
626 InputRecord
= Request
->Data
.WriteConsoleInputRequest
.InputRecord
;
627 Length
= Request
->Data
.WriteConsoleInputRequest
.Length
;
629 if (!Win32CsrValidateBuffer(ProcessData
, InputRecord
, Length
, sizeof(INPUT_RECORD
)))
631 ConioUnlockConsole(Console
);
632 return STATUS_ACCESS_VIOLATION
;
635 for (i
= 0; i
< Length
&& NT_SUCCESS(Status
); i
++)
637 if (!Request
->Data
.WriteConsoleInputRequest
.Unicode
&&
638 InputRecord
->EventType
== KEY_EVENT
)
640 CHAR AsciiChar
= InputRecord
->Event
.KeyEvent
.uChar
.AsciiChar
;
641 ConsoleInputAnsiCharToUnicodeChar(Console
,
642 &InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
,
645 Status
= ConioProcessChar(Console
, InputRecord
++);
648 ConioUnlockConsole(Console
);
650 Request
->Data
.WriteConsoleInputRequest
.Length
= i
;