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
;
42 Buffer
= (PCHAR
)Request
->Data
.ReadConsoleRequest
.Buffer
;
43 UnicodeBuffer
= (PWCHAR
)Buffer
;
44 if (!Win32CsrValidateBuffer(ProcessData
, Buffer
, nNumberOfCharsToRead
, CharSize
))
45 return STATUS_ACCESS_VIOLATION
;
47 if (Request
->Data
.ReadConsoleRequest
.NrCharactersRead
* sizeof(WCHAR
) > nNumberOfCharsToRead
* CharSize
)
48 return STATUS_INVALID_PARAMETER
;
50 Status
= ConioLockConsole(ProcessData
, Request
->Data
.ReadConsoleRequest
.ConsoleHandle
,
51 &Console
, GENERIC_READ
);
52 if (! NT_SUCCESS(Status
))
56 Request
->Data
.ReadConsoleRequest
.EventHandle
= ProcessData
->ConsoleEvent
;
58 Status
= STATUS_PENDING
; /* we haven't read anything (yet) */
59 if (Console
->Mode
& ENABLE_LINE_INPUT
)
61 if (Console
->LineBuffer
== NULL
)
63 /* Starting a new line */
64 Console
->LineMaxSize
= max(256, nNumberOfCharsToRead
);
65 Console
->LineBuffer
= HeapAlloc(Win32CsrApiHeap
, 0, Console
->LineMaxSize
* sizeof(WCHAR
));
66 if (Console
->LineBuffer
== NULL
)
68 Status
= STATUS_NO_MEMORY
;
71 Console
->LineComplete
= FALSE
;
72 Console
->LineUpPressed
= FALSE
;
73 Console
->LineInsertToggle
= 0;
74 Console
->LineWakeupMask
= Request
->Data
.ReadConsoleRequest
.CtrlWakeupMask
;
75 Console
->LineSize
= Request
->Data
.ReadConsoleRequest
.NrCharactersRead
;
76 Console
->LinePos
= Console
->LineSize
;
77 /* pre-filling the buffer is only allowed in the Unicode API,
78 * so we don't need to worry about conversion */
79 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
80 if (Console
->LineSize
== Console
->LineMaxSize
)
82 Console
->LineComplete
= TRUE
;
87 /* If we don't have a complete line yet, process the pending input */
88 while (!Console
->LineComplete
&& !IsListEmpty(&Console
->InputEvents
))
90 /* remove input event from queue */
91 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
92 if (IsListEmpty(&Console
->InputEvents
))
94 ResetEvent(Console
->ActiveEvent
);
96 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
98 /* only pay attention to key down */
99 if (KEY_EVENT
== Input
->InputEvent
.EventType
100 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
102 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
103 Request
->Data
.ReadConsoleRequest
.ControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
105 HeapFree(Win32CsrApiHeap
, 0, Input
);
108 /* Check if we have a complete line to read from */
109 if (Console
->LineComplete
)
111 while (i
< nNumberOfCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
113 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
114 if (Request
->Data
.ReadConsoleRequest
.Unicode
)
115 UnicodeBuffer
[i
++] = Char
;
117 ConsoleInputUnicodeCharToAnsiChar(Console
, &Buffer
[i
++], &Char
);
119 if (Console
->LinePos
== Console
->LineSize
)
121 /* Entire line has been read */
122 HeapFree(Win32CsrApiHeap
, 0, Console
->LineBuffer
);
123 Console
->LineBuffer
= NULL
;
125 Status
= STATUS_SUCCESS
;
130 /* Character input */
131 while (i
< nNumberOfCharsToRead
&& !IsListEmpty(&Console
->InputEvents
))
133 /* remove input event from queue */
134 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
135 if (IsListEmpty(&Console
->InputEvents
))
137 ResetEvent(Console
->ActiveEvent
);
139 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
141 /* only pay attention to valid ascii chars, on key down */
142 if (KEY_EVENT
== Input
->InputEvent
.EventType
143 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
144 && Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
146 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
147 if (Request
->Data
.ReadConsoleRequest
.Unicode
)
148 UnicodeBuffer
[i
++] = Char
;
150 ConsoleInputUnicodeCharToAnsiChar(Console
, &Buffer
[i
++], &Char
);
151 Status
= STATUS_SUCCESS
; /* did read something */
153 HeapFree(Win32CsrApiHeap
, 0, Input
);
157 Request
->Data
.ReadConsoleRequest
.NrCharactersRead
= i
;
158 ConioUnlockConsole(Console
);
164 ConioInputEventToAnsi(PCSRSS_CONSOLE Console
, PINPUT_RECORD InputEvent
)
166 if (InputEvent
->EventType
== KEY_EVENT
)
168 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
169 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
170 ConsoleInputUnicodeCharToAnsiChar(Console
,
171 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
176 static NTSTATUS FASTCALL
177 ConioProcessChar(PCSRSS_CONSOLE Console
,
178 PINPUT_RECORD InputEvent
)
180 ConsoleInput
*ConInRec
;
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 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))
326 if ((ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
) || KeyState
[VK_MENU
] & 0x80) &&
327 (VirtualKeyCode
== VK_ESCAPE
|| VirtualKeyCode
== VK_TAB
|| VirtualKeyCode
== VK_SPACE
))
329 DefWindowProcW( msg
->hwnd
, msg
->message
, msg
->wParam
, msg
->lParam
);
336 DPRINT1("No Active Console!\n");
340 Fake
= UnicodeChar
&&
341 (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
&&
342 msg
->message
!= WM_KEYUP
&& msg
->message
!= WM_SYSKEYUP
);
343 NotChar
= (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
);
345 LastVirtualKey
= msg
->wParam
;
347 DPRINT ("csrss: %s %s %s %s %02x %02x '%lc' %04x\n",
348 Down
? "down" : "up ",
349 (msg
->message
== WM_CHAR
|| msg
->message
== WM_SYSCHAR
) ?
351 Fake
? "fake" : "real",
352 NotChar
? "notc" : "char",
355 (UnicodeChar
>= L
' ') ? UnicodeChar
: L
'.',
361 /* process Ctrl-C and Ctrl-Break */
362 if (Console
->Mode
& ENABLE_PROCESSED_INPUT
&&
363 er
.Event
.KeyEvent
.bKeyDown
&&
364 ((er
.Event
.KeyEvent
.wVirtualKeyCode
== VK_PAUSE
) ||
365 (er
.Event
.KeyEvent
.wVirtualKeyCode
== 'C')) &&
366 (er
.Event
.KeyEvent
.dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
) || KeyState
[VK_CONTROL
] & 0x80))
368 PCSR_PROCESS current
;
369 PLIST_ENTRY current_entry
;
370 DPRINT1("Console_Api Ctrl-C\n");
371 current_entry
= Console
->ProcessList
.Flink
;
372 while (current_entry
!= &Console
->ProcessList
)
374 current
= CONTAINING_RECORD(current_entry
, CSR_PROCESS
, ConsoleLink
);
375 current_entry
= current_entry
->Flink
;
376 ConioConsoleCtrlEvent((DWORD
)CTRL_C_EVENT
, current
);
378 if (Console
->LineBuffer
&& !Console
->LineComplete
)
380 /* Line input is in progress; end it */
381 Console
->LinePos
= Console
->LineSize
= 0;
382 Console
->LineComplete
= TRUE
;
387 if (0 != (er
.Event
.KeyEvent
.dwControlKeyState
388 & (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
))
389 && (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
390 || VK_DOWN
== er
.Event
.KeyEvent
.wVirtualKeyCode
))
392 if (er
.Event
.KeyEvent
.bKeyDown
)
394 /* scroll up or down */
395 if (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
)
397 /* only scroll up if there is room to scroll up into */
398 if (Console
->ActiveBuffer
->CurrentY
!= Console
->ActiveBuffer
->MaxY
- 1)
400 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+
401 Console
->ActiveBuffer
->MaxY
- 1) %
402 Console
->ActiveBuffer
->MaxY
;
403 Console
->ActiveBuffer
->CurrentY
++;
408 /* only scroll down if there is room to scroll down into */
409 if (Console
->ActiveBuffer
->CurrentY
!= 0)
411 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+ 1) %
412 Console
->ActiveBuffer
->MaxY
;
413 Console
->ActiveBuffer
->CurrentY
--;
416 ConioDrawConsole(Console
);
420 ConioProcessChar(Console
, &er
);
423 CSR_API(CsrReadInputEvent
)
425 PLIST_ENTRY CurrentEntry
;
426 PCSRSS_CONSOLE Console
;
428 BOOLEAN Done
= FALSE
;
431 DPRINT("CsrReadInputEvent\n");
433 Request
->Data
.ReadInputRequest
.Event
= ProcessData
->ConsoleEvent
;
435 Status
= ConioLockConsole(ProcessData
, Request
->Data
.ReadInputRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
436 if (! NT_SUCCESS(Status
))
441 /* only get input if there is any */
442 CurrentEntry
= Console
->InputEvents
.Flink
;
443 while (CurrentEntry
!= &Console
->InputEvents
)
445 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
446 CurrentEntry
= CurrentEntry
->Flink
;
450 Request
->Data
.ReadInputRequest
.MoreEvents
= TRUE
;
454 RemoveEntryList(&Input
->ListEntry
);
458 Request
->Data
.ReadInputRequest
.Input
= Input
->InputEvent
;
459 if (Request
->Data
.ReadInputRequest
.Unicode
== FALSE
)
461 ConioInputEventToAnsi(Console
, &Request
->Data
.ReadInputRequest
.Input
);
466 HeapFree(Win32CsrApiHeap
, 0, Input
);
470 Status
= STATUS_SUCCESS
;
472 Status
= STATUS_PENDING
;
474 if (IsListEmpty(&Console
->InputEvents
))
476 ResetEvent(Console
->ActiveEvent
);
479 ConioUnlockConsole(Console
);
484 CSR_API(CsrFlushInputBuffer
)
486 PLIST_ENTRY CurrentEntry
;
487 PCSRSS_CONSOLE Console
;
491 DPRINT("CsrFlushInputBuffer\n");
493 Status
= ConioLockConsole(ProcessData
,
494 Request
->Data
.FlushInputBufferRequest
.ConsoleInput
,
497 if(! NT_SUCCESS(Status
))
502 /* Discard all entries in the input event queue */
503 while (!IsListEmpty(&Console
->InputEvents
))
505 CurrentEntry
= RemoveHeadList(&Console
->InputEvents
);
506 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
507 /* Destroy the event */
508 HeapFree(Win32CsrApiHeap
, 0, Input
);
510 ResetEvent(Console
->ActiveEvent
);
512 ConioUnlockConsole(Console
);
514 return STATUS_SUCCESS
;
517 CSR_API(CsrGetNumberOfConsoleInputEvents
)
520 PCSRSS_CONSOLE Console
;
521 PLIST_ENTRY CurrentItem
;
524 DPRINT("CsrGetNumberOfConsoleInputEvents\n");
526 Status
= ConioLockConsole(ProcessData
, Request
->Data
.GetNumInputEventsRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
527 if (! NT_SUCCESS(Status
))
532 CurrentItem
= Console
->InputEvents
.Flink
;
535 /* If there are any events ... */
536 while (CurrentItem
!= &Console
->InputEvents
)
538 CurrentItem
= CurrentItem
->Flink
;
542 ConioUnlockConsole(Console
);
544 Request
->Data
.GetNumInputEventsRequest
.NumInputEvents
= NumEvents
;
546 return STATUS_SUCCESS
;
549 CSR_API(CsrPeekConsoleInput
)
552 PCSRSS_CONSOLE Console
;
554 PLIST_ENTRY CurrentItem
;
555 PINPUT_RECORD InputRecord
;
559 DPRINT("CsrPeekConsoleInput\n");
561 Status
= ConioLockConsole(ProcessData
, Request
->Data
.GetNumInputEventsRequest
.ConsoleHandle
, &Console
, GENERIC_READ
);
562 if(! NT_SUCCESS(Status
))
567 InputRecord
= Request
->Data
.PeekConsoleInputRequest
.InputRecord
;
568 Length
= Request
->Data
.PeekConsoleInputRequest
.Length
;
570 if (!Win32CsrValidateBuffer(ProcessData
, InputRecord
, Length
, sizeof(INPUT_RECORD
)))
572 ConioUnlockConsole(Console
);
573 return STATUS_ACCESS_VIOLATION
;
578 if (! IsListEmpty(&Console
->InputEvents
))
580 CurrentItem
= Console
->InputEvents
.Flink
;
582 while (CurrentItem
!= &Console
->InputEvents
&& NumItems
< Length
)
584 Item
= CONTAINING_RECORD(CurrentItem
, ConsoleInput
, ListEntry
);
587 *InputRecord
= Item
->InputEvent
;
589 if (Request
->Data
.PeekConsoleInputRequest
.Unicode
== FALSE
)
591 ConioInputEventToAnsi(Console
, InputRecord
);
595 CurrentItem
= CurrentItem
->Flink
;
599 ConioUnlockConsole(Console
);
601 Request
->Data
.PeekConsoleInputRequest
.Length
= NumItems
;
603 return STATUS_SUCCESS
;
606 CSR_API(CsrWriteConsoleInput
)
608 PINPUT_RECORD InputRecord
;
609 PCSRSS_CONSOLE Console
;
614 DPRINT("CsrWriteConsoleInput\n");
616 Status
= ConioLockConsole(ProcessData
, Request
->Data
.WriteConsoleInputRequest
.ConsoleHandle
, &Console
, GENERIC_WRITE
);
617 if (! NT_SUCCESS(Status
))
622 InputRecord
= Request
->Data
.WriteConsoleInputRequest
.InputRecord
;
623 Length
= Request
->Data
.WriteConsoleInputRequest
.Length
;
625 if (!Win32CsrValidateBuffer(ProcessData
, InputRecord
, Length
, sizeof(INPUT_RECORD
)))
627 ConioUnlockConsole(Console
);
628 return STATUS_ACCESS_VIOLATION
;
631 for (i
= 0; i
< Length
&& NT_SUCCESS(Status
); i
++)
633 if (!Request
->Data
.WriteConsoleInputRequest
.Unicode
&&
634 InputRecord
->EventType
== KEY_EVENT
)
636 CHAR AsciiChar
= InputRecord
->Event
.KeyEvent
.uChar
.AsciiChar
;
637 ConsoleInputAnsiCharToUnicodeChar(Console
,
638 &InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
,
641 Status
= ConioProcessChar(Console
, InputRecord
++);
644 ConioUnlockConsole(Console
);
646 Request
->Data
.WriteConsoleInputRequest
.Length
= i
;