Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xwin / winkeybd.c
CommitLineData
a09e091a
JB
1/*
2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
3 *
4 *Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 *"Software"), to deal in the Software without restriction, including
7 *without limitation the rights to use, copy, modify, merge, publish,
8 *distribute, sublicense, and/or sell copies of the Software, and to
9 *permit persons to whom the Software is furnished to do so, subject to
10 *the following conditions:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 *NONINFRINGEMENT. IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE FOR
19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 *Except as contained in this notice, the name of the XFree86 Project
24 *shall not be used in advertising or otherwise to promote the sale, use
25 *or other dealings in this Software without prior written authorization
26 *from the XFree86 Project.
27 *
28 * Authors: Dakshinamurthy Karra
29 * Suhaib M Siddiqi
30 * Peter Busch
31 * Harold L Hunt II
32 */
33
34#ifdef HAVE_XWIN_CONFIG_H
35#include <xwin-config.h>
36#endif
37#include "win.h"
38#include "winkeybd.h"
39#include "winconfig.h"
40#include "winmsg.h"
41
42#include "xkbsrv.h"
43
44/* C does not have a logical XOR operator, so we use a macro instead */
45#define LOGICAL_XOR(a,b) ((!(a) && (b)) || ((a) && !(b)))
46
47static Bool g_winKeyState[NUM_KEYCODES];
48
49/*
50 * Local prototypes
51 */
52
53static void
54 winKeybdBell(int iPercent, DeviceIntPtr pDeviceInt, pointer pCtrl, int iClass);
55
56static void
57 winKeybdCtrl(DeviceIntPtr pDevice, KeybdCtrl * pCtrl);
58
59/*
60 * Translate a Windows WM_[SYS]KEY(UP/DOWN) message
61 * into an ASCII scan code.
62 *
63 * We do this ourselves, rather than letting Windows handle it,
64 * because Windows tends to munge the handling of special keys,
65 * like AltGr on European keyboards.
66 */
67
68int
69winTranslateKey(WPARAM wParam, LPARAM lParam)
70{
71 int iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1];
72 int iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2];
73 int iParam = HIWORD(lParam);
74 int iParamScanCode = LOBYTE(iParam);
75 int iScanCode;
76
77 winDebug("winTranslateKey: wParam %08x lParam %08x\n", wParam, lParam);
78
79/* WM_ key messages faked by Vista speech recognition (WSR) don't have a
80 * scan code.
81 *
82 * Vocola 3 (Rick Mohr's supplement to WSR) uses
83 * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a
84 * scan code of 1
85 */
86 if (iParamScanCode <= 1) {
87 if (VK_PRIOR <= wParam && wParam <= VK_DOWN)
88 /* Trigger special case table to translate to extended
89 * keycode, otherwise if num_lock is on, we can get keypad
90 * numbers instead of navigation keys. */
91 iParam |= KF_EXTENDED;
92 else
93 iParamScanCode = MapVirtualKeyEx(wParam,
94 /*MAPVK_VK_TO_VSC */ 0,
95 GetKeyboardLayout(0));
96 }
97
98 /* Branch on special extended, special non-extended, or normal key */
99 if ((iParam & KF_EXTENDED) && iKeyFixupEx)
100 iScanCode = iKeyFixupEx;
101 else if (iKeyFixup)
102 iScanCode = iKeyFixup;
103 else if (wParam == 0 && iParamScanCode == 0x70)
104 iScanCode = KEY_HKTG;
105 else
106 switch (iParamScanCode) {
107 case 0x70:
108 iScanCode = KEY_HKTG;
109 break;
110 case 0x73:
111 iScanCode = KEY_BSlash2;
112 break;
113 default:
114 iScanCode = iParamScanCode;
115 break;
116 }
117
118 return iScanCode;
119}
120
121/* Ring the keyboard bell (system speaker on PCs) */
122static void
123winKeybdBell(int iPercent, DeviceIntPtr pDeviceInt, pointer pCtrl, int iClass)
124{
125 /*
126 * We can't use Beep () here because it uses the PC speaker
127 * on NT/2000. MessageBeep (MB_OK) will play the default system
128 * sound on systems with a sound card or it will beep the PC speaker
129 * on systems that do not have a sound card.
130 */
131 MessageBeep(MB_OK);
132}
133
134/* Change some keyboard configuration parameters */
135static void
136winKeybdCtrl(DeviceIntPtr pDevice, KeybdCtrl * pCtrl)
137{
138}
139
140/*
141 * See Porting Layer Definition - p. 18
142 * winKeybdProc is known as a DeviceProc.
143 */
144
145int
146winKeybdProc(DeviceIntPtr pDeviceInt, int iState)
147{
148 DevicePtr pDevice = (DevicePtr) pDeviceInt;
149 XkbSrvInfoPtr xkbi;
150 XkbControlsPtr ctrl;
151
152 switch (iState) {
153 case DEVICE_INIT:
154 winConfigKeyboard(pDeviceInt);
155
156 /* FIXME: Maybe we should use winGetKbdLeds () here? */
157 defaultKeyboardControl.leds = g_winInfo.keyboard.leds;
158
159 winErrorFVerb(2, "Rules = \"%s\" Model = \"%s\" Layout = \"%s\""
160 " Variant = \"%s\" Options = \"%s\"\n",
161 g_winInfo.xkb.rules ? g_winInfo.xkb.rules : "none",
162 g_winInfo.xkb.model ? g_winInfo.xkb.model : "none",
163 g_winInfo.xkb.layout ? g_winInfo.xkb.layout : "none",
164 g_winInfo.xkb.variant ? g_winInfo.xkb.variant : "none",
165 g_winInfo.xkb.options ? g_winInfo.xkb.options : "none");
166
167 InitKeyboardDeviceStruct(pDeviceInt,
168 &g_winInfo.xkb, winKeybdBell, winKeybdCtrl);
169
170 xkbi = pDeviceInt->key->xkbInfo;
171 if ((xkbi != NULL) && (xkbi->desc != NULL)) {
172 ctrl = xkbi->desc->ctrls;
173 ctrl->repeat_delay = g_winInfo.keyboard.delay;
174 ctrl->repeat_interval = 1000 / g_winInfo.keyboard.rate;
175 }
176 else {
177 winErrorFVerb(1,
178 "winKeybdProc - Error initializing keyboard AutoRepeat\n");
179 }
180
181 break;
182
183 case DEVICE_ON:
184 pDevice->on = TRUE;
185
186 // immediately copy the state of this keyboard device to the VCK
187 // (which otherwise happens lazily after the first keypress)
188 CopyKeyClass(pDeviceInt, inputInfo.keyboard);
189 break;
190
191 case DEVICE_CLOSE:
192 case DEVICE_OFF:
193 pDevice->on = FALSE;
194 break;
195 }
196
197 return Success;
198}
199
200/*
201 * Detect current mode key states upon server startup.
202 *
203 * Simulate a press and release of any key that is currently
204 * toggled.
205 */
206
207void
208winInitializeModeKeyStates(void)
209{
210 /* Restore NumLock */
211 if (GetKeyState(VK_NUMLOCK) & 0x0001) {
212 winSendKeyEvent(KEY_NumLock, TRUE);
213 winSendKeyEvent(KEY_NumLock, FALSE);
214 }
215
216 /* Restore CapsLock */
217 if (GetKeyState(VK_CAPITAL) & 0x0001) {
218 winSendKeyEvent(KEY_CapsLock, TRUE);
219 winSendKeyEvent(KEY_CapsLock, FALSE);
220 }
221
222 /* Restore ScrollLock */
223 if (GetKeyState(VK_SCROLL) & 0x0001) {
224 winSendKeyEvent(KEY_ScrollLock, TRUE);
225 winSendKeyEvent(KEY_ScrollLock, FALSE);
226 }
227
228 /* Restore KanaLock */
229 if (GetKeyState(VK_KANA) & 0x0001) {
230 winSendKeyEvent(KEY_HKTG, TRUE);
231 winSendKeyEvent(KEY_HKTG, FALSE);
232 }
233}
234
235/*
236 * Upon regaining the keyboard focus we must
237 * resynchronize our internal mode key states
238 * with the actual state of the keys.
239 */
240
241void
242winRestoreModeKeyStates(void)
243{
244 DWORD dwKeyState;
245 BOOL processEvents = TRUE;
246 unsigned short internalKeyStates;
247
248 /* X server is being initialized */
249 if (!inputInfo.keyboard)
250 return;
251
252 /* Only process events if the rootwindow is mapped. The keyboard events
253 * will cause segfaults otherwise */
254 if (screenInfo.screens[0]->root &&
255 screenInfo.screens[0]->root->mapped == FALSE)
256 processEvents = FALSE;
257
258 /* Force to process all pending events in the mi event queue */
259 if (processEvents)
260 mieqProcessInputEvents();
261
262 /* Read the mode key states of our X server */
263 /* (stored in the virtual core keyboard) */
264 internalKeyStates =
265 XkbStateFieldFromRec(&inputInfo.keyboard->key->xkbInfo->state);
266 winDebug("winRestoreModeKeyStates: state %d\n", internalKeyStates);
267
268 /* Check if modifier keys are pressed, and if so, fake a press */
269 {
270
271 BOOL lctrl = (GetAsyncKeyState(VK_LCONTROL) < 0);
272 BOOL rctrl = (GetAsyncKeyState(VK_RCONTROL) < 0);
273 BOOL lshift = (GetAsyncKeyState(VK_LSHIFT) < 0);
274 BOOL rshift = (GetAsyncKeyState(VK_RSHIFT) < 0);
275 BOOL alt = (GetAsyncKeyState(VK_LMENU) < 0);
276 BOOL altgr = (GetAsyncKeyState(VK_RMENU) < 0);
277
278 /*
279 If AltGr and CtrlL appear to be pressed, assume the
280 CtrL is a fake one
281 */
282 if (lctrl && altgr)
283 lctrl = FALSE;
284
285 if (lctrl)
286 winSendKeyEvent(KEY_LCtrl, TRUE);
287
288 if (rctrl)
289 winSendKeyEvent(KEY_RCtrl, TRUE);
290
291 if (lshift)
292 winSendKeyEvent(KEY_ShiftL, TRUE);
293
294 if (rshift)
295 winSendKeyEvent(KEY_ShiftL, TRUE);
296
297 if (alt)
298 winSendKeyEvent(KEY_Alt, TRUE);
299
300 if (altgr)
301 winSendKeyEvent(KEY_AltLang, TRUE);
302 }
303
304 /*
305 Check if latching modifier key states have changed, and if so,
306 fake a press and a release to toggle the modifier to the correct
307 state
308 */
309 dwKeyState = GetKeyState(VK_NUMLOCK) & 0x0001;
310 if (LOGICAL_XOR(internalKeyStates & NumLockMask, dwKeyState)) {
311 winSendKeyEvent(KEY_NumLock, TRUE);
312 winSendKeyEvent(KEY_NumLock, FALSE);
313 }
314
315 dwKeyState = GetKeyState(VK_CAPITAL) & 0x0001;
316 if (LOGICAL_XOR(internalKeyStates & LockMask, dwKeyState)) {
317 winSendKeyEvent(KEY_CapsLock, TRUE);
318 winSendKeyEvent(KEY_CapsLock, FALSE);
319 }
320
321 dwKeyState = GetKeyState(VK_SCROLL) & 0x0001;
322 if (LOGICAL_XOR(internalKeyStates & ScrollLockMask, dwKeyState)) {
323 winSendKeyEvent(KEY_ScrollLock, TRUE);
324 winSendKeyEvent(KEY_ScrollLock, FALSE);
325 }
326
327 dwKeyState = GetKeyState(VK_KANA) & 0x0001;
328 if (LOGICAL_XOR(internalKeyStates & KanaMask, dwKeyState)) {
329 winSendKeyEvent(KEY_HKTG, TRUE);
330 winSendKeyEvent(KEY_HKTG, FALSE);
331 }
332
333 /*
334 For strict correctness, we should also press any non-modifier keys
335 which are already down when we gain focus, but nobody has complained
336 yet :-)
337 */
338}
339
340/*
341 * Look for the lovely fake Control_L press/release generated by Windows
342 * when AltGr is pressed/released on a non-U.S. keyboard.
343 */
344
345Bool
346winIsFakeCtrl_L(UINT message, WPARAM wParam, LPARAM lParam)
347{
348 MSG msgNext;
349 LONG lTime;
350 Bool fReturn;
351
352 static Bool lastWasControlL = FALSE;
353 static LONG lastTime;
354
355 /*
356 * Fake Ctrl_L presses will be followed by an Alt_R press
357 * with the same timestamp as the Ctrl_L press.
358 */
359 if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
360 && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) {
361 /* Got a Ctrl_L press */
362
363 /* Get time of current message */
364 lTime = GetMessageTime();
365
366 /* Look for next press message */
367 fReturn = PeekMessage(&msgNext, NULL,
368 WM_KEYDOWN, WM_SYSKEYDOWN, PM_NOREMOVE);
369
370 if (fReturn && msgNext.message != WM_KEYDOWN &&
371 msgNext.message != WM_SYSKEYDOWN)
372 fReturn = 0;
373
374 if (!fReturn) {
375 lastWasControlL = TRUE;
376 lastTime = lTime;
377 }
378 else {
379 lastWasControlL = FALSE;
380 }
381
382 /* Is next press an Alt_R with the same timestamp? */
383 if (fReturn && msgNext.wParam == VK_MENU
384 && msgNext.time == lTime
385 && (HIWORD(msgNext.lParam) & KF_EXTENDED)) {
386 /*
387 * Next key press is Alt_R with same timestamp as current
388 * Ctrl_L message. Therefore, this Ctrl_L press is a fake
389 * event, so discard it.
390 */
391 return TRUE;
392 }
393 }
394 /*
395 * Sometimes, the Alt_R press message is not yet posted when the
396 * fake Ctrl_L press message arrives (even though it has the
397 * same timestamp), so check for an Alt_R press message that has
398 * arrived since the last Ctrl_L message.
399 */
400 else if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
401 && wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
402 /* Got a Alt_R press */
403
404 if (lastWasControlL) {
405 lTime = GetMessageTime();
406
407 if (lastTime == lTime) {
408 /* Undo the fake Ctrl_L press by sending a fake Ctrl_L release */
409 winSendKeyEvent(KEY_LCtrl, FALSE);
410 }
411 lastWasControlL = FALSE;
412 }
413 }
414 /*
415 * Fake Ctrl_L releases will be followed by an Alt_R release
416 * with the same timestamp as the Ctrl_L release.
417 */
418 else if ((message == WM_KEYUP || message == WM_SYSKEYUP)
419 && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) {
420 /* Got a Ctrl_L release */
421
422 /* Get time of current message */
423 lTime = GetMessageTime();
424
425 /* Look for next release message */
426 fReturn = PeekMessage(&msgNext, NULL,
427 WM_KEYUP, WM_SYSKEYUP, PM_NOREMOVE);
428
429 if (fReturn && msgNext.message != WM_KEYUP &&
430 msgNext.message != WM_SYSKEYUP)
431 fReturn = 0;
432
433 lastWasControlL = FALSE;
434
435 /* Is next press an Alt_R with the same timestamp? */
436 if (fReturn
437 && (msgNext.message == WM_KEYUP || msgNext.message == WM_SYSKEYUP)
438 && msgNext.wParam == VK_MENU
439 && msgNext.time == lTime
440 && (HIWORD(msgNext.lParam) & KF_EXTENDED)) {
441 /*
442 * Next key release is Alt_R with same timestamp as current
443 * Ctrl_L message. Therefore, this Ctrl_L release is a fake
444 * event, so discard it.
445 */
446 return TRUE;
447 }
448 }
449 else {
450 /* On any other press or release message, we don't have a
451 potentially fake Ctrl_L to worry about anymore... */
452 lastWasControlL = FALSE;
453 }
454
455 /* Not a fake control left press/release */
456 return FALSE;
457}
458
459/*
460 * Lift any modifier keys that are pressed
461 */
462
463void
464winKeybdReleaseKeys(void)
465{
466 int i;
467
468#ifdef HAS_DEVWINDOWS
469 /* Verify that the mi input system has been initialized */
470 if (g_fdMessageQueue == WIN_FD_INVALID)
471 return;
472#endif
473
474 /* Loop through all keys */
475 for (i = 0; i < NUM_KEYCODES; ++i) {
476 /* Pop key if pressed */
477 if (g_winKeyState[i])
478 winSendKeyEvent(i, FALSE);
479
480 /* Reset pressed flag for keys */
481 g_winKeyState[i] = FALSE;
482 }
483}
484
485/*
486 * Take a raw X key code and send an up or down event for it.
487 *
488 * Thanks to VNC for inspiration, though it is a simple function.
489 */
490
491void
492winSendKeyEvent(DWORD dwKey, Bool fDown)
493{
494 /*
495 * When alt-tabing between screens we can get phantom key up messages
496 * Here we only pass them through it we think we should!
497 */
498 if (g_winKeyState[dwKey] == FALSE && fDown == FALSE)
499 return;
500
501 /* Update the keyState map */
502 g_winKeyState[dwKey] = fDown;
503
504 QueueKeyboardEvents(g_pwinKeyboard, fDown ? KeyPress : KeyRelease,
505 dwKey + MIN_KEYCODE, NULL);
506
507 winDebug("winSendKeyEvent: dwKey: %d, fDown: %d\n", dwKey, fDown);
508}
509
510BOOL
511winCheckKeyPressed(WPARAM wParam, LPARAM lParam)
512{
513 switch (wParam) {
514 case VK_CONTROL:
515 if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl])
516 return TRUE;
517 if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl])
518 return TRUE;
519 break;
520 case VK_SHIFT:
521 if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR])
522 return TRUE;
523 if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL])
524 return TRUE;
525 break;
526 default:
527 return TRUE;
528 }
529 return FALSE;
530}
531
532/* Only one shift release message is sent even if both are pressed.
533 * Fix this here
534 */
535void
536winFixShiftKeys(int iScanCode)
537{
538 if (GetKeyState(VK_SHIFT) & 0x8000)
539 return;
540
541 if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR])
542 winSendKeyEvent(KEY_ShiftR, FALSE);
543 if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL])
544 winSendKeyEvent(KEY_ShiftL, FALSE);
545}