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