xref: /reactos/sdk/lib/ucrt/conio/getwch.cpp (revision e98e9000)
1 //
2 // getwch.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _getwch(), _getwche(), and _ungetwch(), which get and unget
7 // characters directly from the console.
8 //
9 #include <conio.h>
10 #include <corecrt_internal_lowio.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <wchar.h>
15 
16 
17 
18 namespace
19 {
20     struct CharPair
21     {
22         unsigned char LeadChar;
23         unsigned char SecondChar;
24     };
25 }
26 
27 
28 static wint_t wchbuf = WEOF; // The push-back buffer
29 
30 extern "C" intptr_t __dcrt_lowio_console_input_handle;
31 
32 extern "C" CharPair const* __cdecl _getextendedkeycode(KEY_EVENT_RECORD*);
33 
34 
35 
36 // Reads one character directly from the console.  The _getwche() form also
37 // echoes the character back to the console.  If the push-back buffer is
38 // nonempty, then its value is returned and the push-back buffer is marked as
39 // empty.
40 //
41 // Returns the character that is read on success; returns WEOF on failure.
_getwch()42 extern "C" wint_t __cdecl _getwch()
43 {
44     __acrt_lock(__acrt_conio_lock);
45     wint_t result = 0;
46     __try
47     {
48         result = _getwch_nolock();
49     }
50     __finally
51     {
52         __acrt_unlock(__acrt_conio_lock);
53     }
54     __endtry
55     return result;
56 }
57 
58 
_getwche()59 extern "C" wint_t __cdecl _getwche()
60 {
61     __acrt_lock(__acrt_conio_lock);
62     wint_t result = 0;
63     __try
64     {
65         result = _getwche_nolock();
66     }
67     __finally
68     {
69         __acrt_unlock(__acrt_conio_lock);
70     }
71     __endtry
72     return result;
73 }
74 
75 
76 
_getwch_nolock()77 extern "C" wint_t __cdecl _getwch_nolock()
78 {
79     // First check the pushback buffer for a character.  If it has one, return
80     // it without echoing and reset the buffer:
81     if (wchbuf != WEOF)
82     {
83         wchar_t const buffered_wchar = static_cast<wchar_t>(wchbuf & 0xFFFF);
84         wchbuf = WEOF;
85         return buffered_wchar;
86     }
87 
88     // The console input handle is created the first time that _getwch(),
89     // _cgetws(), or _kbhit() is called:
90     if (__dcrt_lowio_ensure_console_input_initialized() == FALSE)
91         return WEOF;
92 
93     // Switch to raw mode (no line input, no echo input):
94     DWORD old_console_mode;
95     __dcrt_get_input_console_mode(&old_console_mode);
96     __dcrt_set_input_console_mode(0);
97     wint_t result = 0;
98     __try
99     {
100         for ( ; ; )
101         {
102             // Get a console input event:
103             INPUT_RECORD input_record;
104             DWORD num_read;
105             if (__dcrt_read_console_input(&input_record, 1, &num_read) == FALSE)
106             {
107                 result = WEOF;
108                 __leave;
109             }
110 
111             if (num_read == 0)
112             {
113                 result = WEOF;
114                 __leave;
115             }
116 
117             // Look for, and decipher, key events.
118             if (input_record.EventType == KEY_EVENT && input_record.Event.KeyEvent.bKeyDown)
119             {
120                 // Easy case:  if UnicodeChar is non-zero, we can just return it:
121                 wchar_t const c = static_cast<wchar_t>(input_record.Event.KeyEvent.uChar.UnicodeChar);
122                 if (c != 0)
123                 {
124                     result = c;
125                     __leave;
126                 }
127 
128                 // Hard case:  either it is an extended code or an event which
129                 // should not be recognized.  Let _getextendedkeycode do the work:
130                 CharPair const* const cp = _getextendedkeycode(&input_record.Event.KeyEvent);
131                 if (cp != nullptr)
132                 {
133                     wchbuf = cp->SecondChar;
134                     result = cp->LeadChar;
135                     __leave;
136                 }
137             }
138         }
139     }
140     __finally
141     {
142         // Restore the previous console mode:
143         __dcrt_set_input_console_mode(old_console_mode);
144     }
145     __endtry
146     return result;
147 }
148 
149 
150 
_getwche_nolock()151 extern "C" wint_t __cdecl _getwche_nolock()
152 {
153     // First check the pushback buffer for a character.  If it has one, return
154     // it without echoing and reset the buffer:
155     if (wchbuf != WEOF)
156     {
157         wchar_t const buffered_wchar = static_cast<wchar_t>(wchbuf & 0xFFFF);
158         wchbuf = WEOF;
159         return buffered_wchar;
160     }
161 
162     // Othwrwise, read the character, echo it, and return it.  If anything fails,
163     // we immediately return WEOF.
164     wchar_t const gotten_wchar = _getwch_nolock();
165     if (gotten_wchar == WEOF)
166         return WEOF;
167 
168     if (_putwch_nolock(gotten_wchar) == WEOF)
169         return WEOF;
170 
171     return gotten_wchar;
172 }
173 
174 
175 
176 // Pushes back ("ungets") one character to be read next by _getwch() or
177 // _getwche().  On success, returns the character that was pushed back; on
178 // failure, returns EOF.
_ungetwch(wint_t const c)179 extern "C" wint_t __cdecl _ungetwch(wint_t const c)
180 {
181     __acrt_lock(__acrt_conio_lock);
182     wint_t result = 0;
183     __try
184     {
185         result = _ungetwch_nolock(c);
186     }
187     __finally
188     {
189         __acrt_unlock(__acrt_conio_lock);
190     }
191     __endtry
192     return result;
193 }
194 
195 
196 
_ungetwch_nolock(wint_t const c)197 extern "C" wint_t __cdecl _ungetwch_nolock(wint_t const c)
198 {
199     // Fail if the char is EOF or the pushback buffer is non-empty:
200     if (c == WEOF || wchbuf != WEOF)
201         return static_cast<wint_t>(EOF);
202 
203     wchbuf = (c & 0xFF);
204     return wchbuf;
205 }
206