xref: /reactos/base/setup/usetup/keytrans.c (revision 80733143)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/usetup/keytrans.c
23  * PURPOSE:         Console support functions: keyboard translation
24  * PROGRAMMER:      Tinus
25  *
26  * NB: Hardcoded to US keyboard
27  */
28 #include <usetup.h>
29 #include "keytrans.h"
30 
31 #define NDEBUG
32 #include <debug.h>
33 
34 static WORD KeyTable[] = {
35 /* 0x00 */
36 	0x00,		VK_ESCAPE,	0x31,		0x32,
37 	0x33,		0x34,		0x35,		0x36,
38 	0x37,		0x38,		0x39,		0x30,
39 	VK_OEM_MINUS,	VK_OEM_PLUS,	VK_BACK,	VK_TAB,
40 /* 0x10 */
41 	0x51,		0x57,		0x45,		0x52,
42 	0x54,		0x59,		0x55,		0x49,
43 	0x4f,		0x50,		VK_OEM_4,	VK_OEM_6,
44 	VK_RETURN,	VK_CONTROL,	0x41,		0x53,
45 /* 0x20 */
46 	0x44,		0x46,		0x47,		0x48,
47 	0x4a,		0x4b,		0x4c,		VK_OEM_1,
48 	VK_OEM_7,	0xc0,		VK_LSHIFT,	VK_OEM_5,
49 	0x5a,		0x58,		0x43,		0x56,
50 /* 0x30 */
51 	0x42,		0x4e,		0x4d,		VK_OEM_COMMA,
52 	VK_OEM_PERIOD,	VK_OEM_2,	VK_RSHIFT,	VK_MULTIPLY,
53 	VK_LMENU,	VK_SPACE,	VK_CAPITAL,	VK_F1,
54 	VK_F2,		VK_F3,		VK_F4,		VK_F5,
55 /* 0x40 */
56 	VK_F6,		VK_F7,		VK_F8,		VK_F9,
57 	VK_F10,		VK_NUMLOCK,	VK_SCROLL,	VK_HOME,
58 	VK_UP,		VK_PRIOR,	VK_SUBTRACT,	VK_LEFT,
59 	0,		VK_RIGHT,	VK_ADD,		VK_END,
60 /* 0x50 */
61 	VK_DOWN,	VK_NEXT,	VK_INSERT,	VK_DELETE,
62 	0,		0,		0,		VK_F11,
63 	VK_F12,		0,		0,		0,
64 	0,		0,		0,		0,
65 /* 0x60 */
66 	0,		0,		0,		0,
67 	0,		0,		0,		0,
68 	0,		0,		0,		0,
69 	0,		0,		0,		0,
70 /* 0x70 */
71 	0,		0,		0,		0,
72 	0,		0,		0,		0,
73 	0,		0,		0,		0,
74 	0,		0,		0,		0
75 };
76 
77 static WORD KeyTableEnhanced[] = {
78 /* 0x00 */
79 	0,		0,		0,		0,
80 	0,		0,		0,		0,
81 	0,		0,		0,		0,
82 	0,		0,		0,		0,
83 /* 0x10 */
84 	0,		0,		0,		0,
85 	0,		0,		0,		0,
86 	0,		0,		0,		0,
87 	VK_RETURN,	VK_RCONTROL,	0,		0,
88 /* 0x20 */
89 	0,		0,		0,		0,
90 	0,		0,		0,		0,
91 	0,		0,		0,		0,
92 	0,		0,		0,		0,
93 /* 0x30 */
94 	0,		0,		0,		0,
95 	0,		VK_DIVIDE,	0,		VK_SNAPSHOT,
96 	VK_RMENU,	0,		0,		0,
97 	0,		0,		0,		0,
98 /* 0x40 */
99 	0,		0,		0,		0,
100 	0,		0,		0,		VK_HOME,
101 	VK_UP,		VK_PRIOR,	0,		VK_LEFT,
102 	0,		VK_RIGHT,	0,		VK_END,
103 /* 0x50 */
104 	VK_DOWN,	VK_NEXT,	VK_INSERT,	VK_DELETE,
105 	0,		0,		0,		0,
106 	0,		0,		0,		0,
107 	0,		0,		0,		0,
108 /* 0x60 */
109 	0,		0,		0,		0,
110 	0,		0,		0,		0,
111 	0,		0,		0,		0,
112 	0,		0,		0,		0,
113 /* 0x70 */
114 	0,		0,		0,		0,
115 	0,		0,		0,		0,
116 	0,		0,		0,		0,
117 	0,		0,		0,		0
118 };
119 
120 static WORD KeyTableNumlock[] = {
121 /* 0x00 */
122 	0,		0,		0,		0,
123 	0,		0,		0,		0,
124 	0,		0,		0,		0,
125 	0,		0,		0,		0,
126 /* 0x10 */
127 	0,		0,		0,		0,
128 	0,		0,		0,		0,
129 	0,		0,		0,		0,
130 	0,		0,		0,		0,
131 /* 0x20 */
132 	0,		0,		0,		0,
133 	0,		0,		0,		0,
134 	0,		0,		0,		0,
135 	0,		0,		0,		0,
136 /* 0x30 */
137 	0,		0,		0,		0,
138 	0,		0,		0,		0,
139 	0,		0,		0,		0,
140 	0,		0,		0,		0,
141 /* 0x40 */
142 	0,		0,		0,		0,
143 	0,		0,		0,		VK_NUMPAD7,
144 	VK_NUMPAD8,	VK_NUMPAD9,	0,		VK_NUMPAD4,
145 	VK_NUMPAD5,	VK_NUMPAD6,	0,		VK_NUMPAD1,
146 /* 0x50 */
147 	VK_NUMPAD2,	VK_NUMPAD3,	VK_NUMPAD0,	0,
148 	0,		0,		0,		0,
149 	0,		0,		0,		0,
150 	0,		0,		0,		0,
151 /* 0x60 */
152 	0,		0,		0,		0,
153 	0,		0,		0,		0,
154 	0,		0,		0,		0,
155 	0,		0,		0,		0,
156 /* 0x70 */
157 	0,		0,		0,		0,
158 	0,		0,		0,		0,
159 	0,		0,		0,		0,
160 	0,		0,		0,		0
161 };
162 
163 typedef struct _SCANTOASCII {
164 	USHORT ScanCode;
165 	USHORT Enhanced;
166 	UCHAR Normal;
167 	UCHAR Shift;
168 	UCHAR NumLock;
169 	UCHAR bCAPS;
170 } SCANTOASCII, *PSCANTOASCII;
171 
172 SCANTOASCII ScanToAscii[] = {
173 {	0x1e,	0,	'a',	'A',	0, TRUE  },
174 {	0x30,	0,	'b',	'B',	0, TRUE  },
175 {	0x2e,	0,	'c',	'C',	0, TRUE  },
176 {	0x20,	0,	'd',	'D',	0, TRUE  },
177 {	0x12,	0,	'e',	'E',	0, TRUE  },
178 {	0x21,	0,	'f',	'F',	0, TRUE  },
179 {	0x22,	0,	'g',	'G',	0, TRUE  },
180 {	0x23,	0,	'h',	'H',	0, TRUE  },
181 {	0x17,	0,	'i',	'I',	0, TRUE  },
182 {	0x24,	0,	'j',	'J',	0, TRUE  },
183 {	0x25,	0,	'k',	'K',	0, TRUE  },
184 {	0x26,	0,	'l',	'L',	0, TRUE  },
185 {	0x32,	0,	'm',	'M',	0, TRUE  },
186 {	0x31,	0,	'n',	'N',	0, TRUE  },
187 {	0x18,	0,	'o',	'O',	0, TRUE  },
188 {	0x19,	0,	'p',	'P',	0, TRUE  },
189 {	0x10,	0,	'q',	'Q',	0, TRUE  },
190 {	0x13,	0,	'r',	'R',	0, TRUE  },
191 {	0x1f,	0,	's',	'S',	0, TRUE  },
192 {	0x14,	0,	't',	'T',	0, TRUE  },
193 {	0x16,	0,	'u',	'U',	0, TRUE  },
194 {	0x2f,	0,	'v',	'V',	0, TRUE  },
195 {	0x11,	0,	'w',	'W',	0, TRUE  },
196 {	0x2d,	0,	'x',	'X',	0, TRUE  },
197 {	0x15,	0,	'y',	'Y',	0, TRUE  },
198 {	0x2c,	0,	'z',	'Z',	0, TRUE  },
199 
200 {	0x02,	0,	'1',	'!',	0, FALSE },
201 {	0x03,	0,	'2',	'@',	0, FALSE },
202 {	0x04,	0,	'3',	'#',	0, FALSE },
203 {	0x05,	0,	'4',	'$',	0, FALSE },
204 {	0x06,	0,	'5',	'%',	0, FALSE },
205 {	0x07,	0,	'6',	'^',	0, FALSE },
206 {	0x08,	0,	'7',	'&',	0, FALSE },
207 {	0x09,	0,	'8',	'*',	0, FALSE },
208 {	0x0a,	0,	'9',	'(',	0, FALSE },
209 {	0x0b,	0,	'0',	')',	0, FALSE },
210 
211 {	0x29,	0,	'\'',	'~',	0, FALSE },
212 {	0x0c,	0,	'-',	'_',	0, FALSE },
213 {	0x0d,	0,	'=',	'+',	0, FALSE },
214 {	0x1a,	0,	'[',	'{',	0, FALSE },
215 {	0x1b,	0,	']',	'}',	0, FALSE },
216 {	0x2b,	0,	'\\',	'|',	0, FALSE },
217 {	0x27,	0,	';',	':',	0, FALSE },
218 {	0x28,	0,	'\'',	'"',	0, FALSE },
219 {	0x33,	0,	',',	'<',	0, FALSE },
220 {	0x34,	0,	'.',	'>',	0, FALSE },
221 {	0x35,	0,	'/',	'?',	0, FALSE },
222 
223 {	0x4f,	0,	0,	0,	'1', FALSE },
224 {	0x50,	0,	0,	0,	'2', FALSE },
225 {	0x51,	0,	0,	0,	'3', FALSE },
226 {	0x4b,	0,	0,	0,	'4', FALSE },
227 {	0x4c,	0,	0,	0,	'5', FALSE },
228 {	0x4d,	0,	0,	0,	'6', FALSE },
229 {	0x47,	0,	0,	0,	'7', FALSE },
230 {	0x48,	0,	0,	0,	'8', FALSE },
231 {	0x49,	0,	0,	0,	'9', FALSE },
232 {	0x52,	0,	0,	0,	'0', FALSE },
233 
234 {	0x4a,	0,	'-',	'-',	0, FALSE },
235 {	0x4e,	0,	'+',	'+',	0, FALSE },
236 {	0x37,	0,	'*',	'*',	0, FALSE },
237 {	0x35,	1,	'/',	'/',	0, FALSE },
238 {	0x53,	0,	0,	0,	'.', FALSE },
239 
240 {	0x39,	0,	' ',	' ',	0, FALSE },
241 
242 {	0x1c,	0,	'\r',	'\r',	0, FALSE },
243 {	0x1c,	1,	'\r',	'\r',	0, FALSE },
244 {	0x0e,	0,	0x08,	0x08,	0, FALSE }, /* backspace */
245 
246 {	0,	0,	0,	0,	0, FALSE }
247 };
248 
249 
250 static void
251 IntUpdateControlKeyState(HANDLE hConsoleInput, LPDWORD State, PKEYBOARD_INPUT_DATA InputData)
252 {
253 	DWORD Value = 0;
254     DWORD oldState, newState;
255 
256 	if (InputData->Flags & KEY_E1) /* Only the pause key has E1 */
257 		return;
258 
259     oldState = newState = *State;
260 
261 	if (!(InputData->Flags & KEY_E0)) {
262 		switch (InputData->MakeCode) {
263 			case 0x2a:
264 			case 0x36:
265 				Value = SHIFT_PRESSED;
266 				break;
267 
268 			case 0x1d:
269 				Value = LEFT_CTRL_PRESSED;
270 				break;
271 
272 			case 0x38:
273 				Value = LEFT_ALT_PRESSED;
274 				break;
275 
276 			case 0x3A:
277 				if (!(InputData->Flags & KEY_BREAK))
278 					newState ^= CAPSLOCK_ON;
279 				break;
280 
281 			case 0x45:
282 				if (!(InputData->Flags & KEY_BREAK))
283 					newState ^= NUMLOCK_ON;
284 				break;
285 
286 			case 0x46:
287 				if (!(InputData->Flags & KEY_BREAK))
288 					newState ^= SCROLLLOCK_ON;
289 				break;
290 
291 			default:
292 				return;
293 		}
294 	} else {
295 		switch (InputData->MakeCode) {
296 			case 0x1d:
297 				Value = RIGHT_CTRL_PRESSED;
298 				break;
299 
300 			case 0x38:
301 				Value = RIGHT_ALT_PRESSED;
302 				break;
303 
304 			default:
305 				return;
306 		}
307 	}
308 
309     /* Check if the state of the indicators has been changed */
310     if ((oldState ^ newState) & (NUMLOCK_ON | CAPSLOCK_ON | SCROLLLOCK_ON))
311     {
312         IO_STATUS_BLOCK               IoStatusBlock;
313         NTSTATUS                      Status;
314         KEYBOARD_INDICATOR_PARAMETERS kip;
315 
316         kip.LedFlags = 0;
317         kip.UnitId   = 0;
318 
319         if ((newState & NUMLOCK_ON))
320             kip.LedFlags |= KEYBOARD_NUM_LOCK_ON;
321 
322         if ((newState & CAPSLOCK_ON))
323             kip.LedFlags |= KEYBOARD_CAPS_LOCK_ON;
324 
325         if ((newState & SCROLLLOCK_ON))
326             kip.LedFlags |= KEYBOARD_SCROLL_LOCK_ON;
327 
328         /* Update the state of the leds on primary keyboard */
329         DPRINT("NtDeviceIoControlFile dwLeds=%x\n", kip.LedFlags);
330 
331         Status = NtDeviceIoControlFile(
332               hConsoleInput,
333               NULL,
334               NULL,
335               NULL,
336               &IoStatusBlock,
337               IOCTL_KEYBOARD_SET_INDICATORS,
338 		      &kip,
339               sizeof(kip),
340 		      NULL,
341               0);
342 
343         if (!NT_SUCCESS(Status))
344         {
345             DPRINT1("NtDeviceIoControlFile(IOCTL_KEYBOARD_SET_INDICATORS) failed (Status %lx)\n", Status);
346         }
347     } else
348     /* Normal press/release state handling */
349 	if (InputData->Flags & KEY_BREAK)
350 		newState &= ~Value;
351 	else
352 		newState |= Value;
353 
354     *State = newState;
355 }
356 
357 static DWORD
358 IntVKFromKbdInput(PKEYBOARD_INPUT_DATA InputData, DWORD KeyState)
359 {
360 	if (!(KeyState & ENHANCED_KEY)) {
361 		if ((KeyState & NUMLOCK_ON) &&
362 		    KeyTableNumlock[InputData->MakeCode & 0x7f]) {
363 			DPRINT("Numlock, using %x\n",
364 			       InputData->MakeCode & 0x7f);
365 			return KeyTableNumlock[InputData->MakeCode & 0x7f];
366 		}
367 		DPRINT("Not enhanced, using %x\n", InputData->MakeCode & 0x7f);
368 		return KeyTable[InputData->MakeCode & 0x7f];
369 	}
370 
371 	DPRINT("Enhanced, using %x\n", InputData->MakeCode & 0x7f);
372 	return KeyTableEnhanced[InputData->MakeCode & 0x7f];
373 }
374 
375 static UCHAR
376 IntAsciiFromInput(PKEYBOARD_INPUT_DATA InputData, DWORD KeyState)
377 {
378 	UINT Counter = 0;
379 	USHORT Enhanced = 0;
380 
381 	if (KeyState & ENHANCED_KEY) Enhanced = 1;
382 
383 	while (ScanToAscii[Counter].ScanCode != 0) {
384 		if ((ScanToAscii[Counter].ScanCode == InputData->MakeCode)  &&
385 		    (ScanToAscii[Counter].Enhanced == Enhanced)) {
386 			if (ScanToAscii[Counter].NumLock) {
387 				if ((KeyState & NUMLOCK_ON) &&
388 				    !(KeyState & SHIFT_PRESSED)) {
389 					return ScanToAscii[Counter].NumLock;
390 				} else {
391 					return ScanToAscii[Counter].Normal;
392 				}
393 			}
394 
395 			if ((KeyState & CAPSLOCK_ON) && ScanToAscii[Counter].bCAPS)
396 				KeyState ^= SHIFT_PRESSED;
397 
398 			if (KeyState & SHIFT_PRESSED)
399 				return ScanToAscii[Counter].Shift;
400 
401 			return ScanToAscii[Counter].Normal;
402 		}
403 		Counter++;
404 	}
405 
406 	return 0;
407 }
408 
409 /* This is going to be quick and messy. The usetup app runs in native mode
410  * so it cannot use the translation routines in win32k which means it'll have
411  * to be done here too.
412  *
413  * Only the bKeyDown, AsciiChar and wVirtualKeyCode members are used
414  * in the app so I'll just fill the others with somewhat sane values
415  */
416 NTSTATUS
417 IntTranslateKey(HANDLE hConsoleInput, PKEYBOARD_INPUT_DATA InputData, KEY_EVENT_RECORD *Event)
418 {
419 	static DWORD dwControlKeyState;
420 
421 	RtlZeroMemory(Event, sizeof(KEY_EVENT_RECORD));
422 
423 	if (!(InputData->Flags & KEY_BREAK))
424 		Event->bKeyDown = TRUE;
425 	else
426 		Event->bKeyDown = FALSE;
427 
428 	Event->wRepeatCount = 1;
429 	Event->wVirtualScanCode = InputData->MakeCode;
430 
431 	DPRINT("Translating: %x\n", InputData->MakeCode);
432 
433 	IntUpdateControlKeyState(hConsoleInput, &dwControlKeyState, InputData);
434 	Event->dwControlKeyState = dwControlKeyState;
435 
436 	if (InputData->Flags & KEY_E0)
437 		Event->dwControlKeyState |= ENHANCED_KEY;
438 
439 	Event->wVirtualKeyCode = IntVKFromKbdInput(InputData,
440 	                                           Event->dwControlKeyState);
441 
442 	DPRINT("Result: %x\n", Event->wVirtualKeyCode);
443 
444 	if (Event->bKeyDown) {
445 		Event->uChar.AsciiChar =
446 		                   IntAsciiFromInput(InputData,
447 		                                     Event->dwControlKeyState);
448 		DPRINT("Char: %x\n", Event->uChar.AsciiChar);
449 	} else {
450 		Event->uChar.AsciiChar = 0;
451 	}
452 
453 	return STATUS_SUCCESS;
454 }
455