2 *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved.
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:
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
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.
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.
28 * Authors: Dakshinamurthy Karra
34 #ifdef HAVE_XWIN_CONFIG_H
35 #include <xwin-config.h>
39 #include "winconfig.h"
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)))
47 static Bool g_winKeyState
[NUM_KEYCODES
];
54 winKeybdBell(int iPercent
, DeviceIntPtr pDeviceInt
, pointer pCtrl
, int iClass
);
57 winKeybdCtrl(DeviceIntPtr pDevice
, KeybdCtrl
* pCtrl
);
60 * Translate a Windows WM_[SYS]KEY(UP/DOWN) message
61 * into an ASCII scan code.
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.
69 winTranslateKey(WPARAM wParam
, LPARAM lParam
)
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
);
77 winDebug("winTranslateKey: wParam %08x lParam %08x\n", wParam
, lParam
);
79 /* WM_ key messages faked by Vista speech recognition (WSR) don't have a
82 * Vocola 3 (Rick Mohr's supplement to WSR) uses
83 * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a
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
;
93 iParamScanCode
= MapVirtualKeyEx(wParam
,
94 /*MAPVK_VK_TO_VSC */ 0,
95 GetKeyboardLayout(0));
98 /* Branch on special extended, special non-extended, or normal key */
99 if ((iParam
& KF_EXTENDED
) && iKeyFixupEx
)
100 iScanCode
= iKeyFixupEx
;
102 iScanCode
= iKeyFixup
;
103 else if (wParam
== 0 && iParamScanCode
== 0x70)
104 iScanCode
= KEY_HKTG
;
106 switch (iParamScanCode
) {
108 iScanCode
= KEY_HKTG
;
111 iScanCode
= KEY_BSlash2
;
114 iScanCode
= iParamScanCode
;
121 /* Ring the keyboard bell (system speaker on PCs) */
123 winKeybdBell(int iPercent
, DeviceIntPtr pDeviceInt
, pointer pCtrl
, int iClass
)
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.
134 /* Change some keyboard configuration parameters */
136 winKeybdCtrl(DeviceIntPtr pDevice
, KeybdCtrl
* pCtrl
)
141 * See Porting Layer Definition - p. 18
142 * winKeybdProc is known as a DeviceProc.
146 winKeybdProc(DeviceIntPtr pDeviceInt
, int iState
)
148 DevicePtr pDevice
= (DevicePtr
) pDeviceInt
;
154 winConfigKeyboard(pDeviceInt
);
156 /* FIXME: Maybe we should use winGetKbdLeds () here? */
157 defaultKeyboardControl
.leds
= g_winInfo
.keyboard
.leds
;
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");
167 InitKeyboardDeviceStruct(pDeviceInt
,
168 &g_winInfo
.xkb
, winKeybdBell
, winKeybdCtrl
);
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
;
178 "winKeybdProc - Error initializing keyboard AutoRepeat\n");
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
);
201 * Detect current mode key states upon server startup.
203 * Simulate a press and release of any key that is currently
208 winInitializeModeKeyStates(void)
210 /* Restore NumLock */
211 if (GetKeyState(VK_NUMLOCK
) & 0x0001) {
212 winSendKeyEvent(KEY_NumLock
, TRUE
);
213 winSendKeyEvent(KEY_NumLock
, FALSE
);
216 /* Restore CapsLock */
217 if (GetKeyState(VK_CAPITAL
) & 0x0001) {
218 winSendKeyEvent(KEY_CapsLock
, TRUE
);
219 winSendKeyEvent(KEY_CapsLock
, FALSE
);
222 /* Restore ScrollLock */
223 if (GetKeyState(VK_SCROLL
) & 0x0001) {
224 winSendKeyEvent(KEY_ScrollLock
, TRUE
);
225 winSendKeyEvent(KEY_ScrollLock
, FALSE
);
228 /* Restore KanaLock */
229 if (GetKeyState(VK_KANA
) & 0x0001) {
230 winSendKeyEvent(KEY_HKTG
, TRUE
);
231 winSendKeyEvent(KEY_HKTG
, FALSE
);
236 * Upon regaining the keyboard focus we must
237 * resynchronize our internal mode key states
238 * with the actual state of the keys.
242 winRestoreModeKeyStates(void)
245 BOOL processEvents
= TRUE
;
246 unsigned short internalKeyStates
;
248 /* X server is being initialized */
249 if (!inputInfo
.keyboard
)
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
;
258 /* Force to process all pending events in the mi event queue */
260 mieqProcessInputEvents();
262 /* Read the mode key states of our X server */
263 /* (stored in the virtual core keyboard) */
265 XkbStateFieldFromRec(&inputInfo
.keyboard
->key
->xkbInfo
->state
);
266 winDebug("winRestoreModeKeyStates: state %d\n", internalKeyStates
);
268 /* Check if modifier keys are pressed, and if so, fake a press */
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);
279 If AltGr and CtrlL appear to be pressed, assume the
286 winSendKeyEvent(KEY_LCtrl
, TRUE
);
289 winSendKeyEvent(KEY_RCtrl
, TRUE
);
292 winSendKeyEvent(KEY_ShiftL
, TRUE
);
295 winSendKeyEvent(KEY_ShiftL
, TRUE
);
298 winSendKeyEvent(KEY_Alt
, TRUE
);
301 winSendKeyEvent(KEY_AltLang
, TRUE
);
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
309 dwKeyState
= GetKeyState(VK_NUMLOCK
) & 0x0001;
310 if (LOGICAL_XOR(internalKeyStates
& NumLockMask
, dwKeyState
)) {
311 winSendKeyEvent(KEY_NumLock
, TRUE
);
312 winSendKeyEvent(KEY_NumLock
, FALSE
);
315 dwKeyState
= GetKeyState(VK_CAPITAL
) & 0x0001;
316 if (LOGICAL_XOR(internalKeyStates
& LockMask
, dwKeyState
)) {
317 winSendKeyEvent(KEY_CapsLock
, TRUE
);
318 winSendKeyEvent(KEY_CapsLock
, FALSE
);
321 dwKeyState
= GetKeyState(VK_SCROLL
) & 0x0001;
322 if (LOGICAL_XOR(internalKeyStates
& ScrollLockMask
, dwKeyState
)) {
323 winSendKeyEvent(KEY_ScrollLock
, TRUE
);
324 winSendKeyEvent(KEY_ScrollLock
, FALSE
);
327 dwKeyState
= GetKeyState(VK_KANA
) & 0x0001;
328 if (LOGICAL_XOR(internalKeyStates
& KanaMask
, dwKeyState
)) {
329 winSendKeyEvent(KEY_HKTG
, TRUE
);
330 winSendKeyEvent(KEY_HKTG
, FALSE
);
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
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.
346 winIsFakeCtrl_L(UINT message
, WPARAM wParam
, LPARAM lParam
)
352 static Bool lastWasControlL
= FALSE
;
353 static LONG lastTime
;
356 * Fake Ctrl_L presses will be followed by an Alt_R press
357 * with the same timestamp as the Ctrl_L press.
359 if ((message
== WM_KEYDOWN
|| message
== WM_SYSKEYDOWN
)
360 && wParam
== VK_CONTROL
&& (HIWORD(lParam
) & KF_EXTENDED
) == 0) {
361 /* Got a Ctrl_L press */
363 /* Get time of current message */
364 lTime
= GetMessageTime();
366 /* Look for next press message */
367 fReturn
= PeekMessage(&msgNext
, NULL
,
368 WM_KEYDOWN
, WM_SYSKEYDOWN
, PM_NOREMOVE
);
370 if (fReturn
&& msgNext
.message
!= WM_KEYDOWN
&&
371 msgNext
.message
!= WM_SYSKEYDOWN
)
375 lastWasControlL
= TRUE
;
379 lastWasControlL
= FALSE
;
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
)) {
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.
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.
400 else if ((message
== WM_KEYDOWN
|| message
== WM_SYSKEYDOWN
)
401 && wParam
== VK_MENU
&& (HIWORD(lParam
) & KF_EXTENDED
)) {
402 /* Got a Alt_R press */
404 if (lastWasControlL
) {
405 lTime
= GetMessageTime();
407 if (lastTime
== lTime
) {
408 /* Undo the fake Ctrl_L press by sending a fake Ctrl_L release */
409 winSendKeyEvent(KEY_LCtrl
, FALSE
);
411 lastWasControlL
= FALSE
;
415 * Fake Ctrl_L releases will be followed by an Alt_R release
416 * with the same timestamp as the Ctrl_L release.
418 else if ((message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
419 && wParam
== VK_CONTROL
&& (HIWORD(lParam
) & KF_EXTENDED
) == 0) {
420 /* Got a Ctrl_L release */
422 /* Get time of current message */
423 lTime
= GetMessageTime();
425 /* Look for next release message */
426 fReturn
= PeekMessage(&msgNext
, NULL
,
427 WM_KEYUP
, WM_SYSKEYUP
, PM_NOREMOVE
);
429 if (fReturn
&& msgNext
.message
!= WM_KEYUP
&&
430 msgNext
.message
!= WM_SYSKEYUP
)
433 lastWasControlL
= FALSE
;
435 /* Is next press an Alt_R with the same timestamp? */
437 && (msgNext
.message
== WM_KEYUP
|| msgNext
.message
== WM_SYSKEYUP
)
438 && msgNext
.wParam
== VK_MENU
439 && msgNext
.time
== lTime
440 && (HIWORD(msgNext
.lParam
) & KF_EXTENDED
)) {
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.
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
;
455 /* Not a fake control left press/release */
460 * Lift any modifier keys that are pressed
464 winKeybdReleaseKeys(void)
468 #ifdef HAS_DEVWINDOWS
469 /* Verify that the mi input system has been initialized */
470 if (g_fdMessageQueue
== WIN_FD_INVALID
)
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
);
480 /* Reset pressed flag for keys */
481 g_winKeyState
[i
] = FALSE
;
486 * Take a raw X key code and send an up or down event for it.
488 * Thanks to VNC for inspiration, though it is a simple function.
492 winSendKeyEvent(DWORD dwKey
, Bool fDown
)
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!
498 if (g_winKeyState
[dwKey
] == FALSE
&& fDown
== FALSE
)
501 /* Update the keyState map */
502 g_winKeyState
[dwKey
] = fDown
;
504 QueueKeyboardEvents(g_pwinKeyboard
, fDown
? KeyPress
: KeyRelease
,
505 dwKey
+ MIN_KEYCODE
, NULL
);
507 winDebug("winSendKeyEvent: dwKey: %d, fDown: %d\n", dwKey
, fDown
);
511 winCheckKeyPressed(WPARAM wParam
, LPARAM lParam
)
515 if ((lParam
& 0x1ff0000) == 0x11d0000 && g_winKeyState
[KEY_RCtrl
])
517 if ((lParam
& 0x1ff0000) == 0x01d0000 && g_winKeyState
[KEY_LCtrl
])
521 if ((lParam
& 0x1ff0000) == 0x0360000 && g_winKeyState
[KEY_ShiftR
])
523 if ((lParam
& 0x1ff0000) == 0x02a0000 && g_winKeyState
[KEY_ShiftL
])
532 /* Only one shift release message is sent even if both are pressed.
536 winFixShiftKeys(int iScanCode
)
538 if (GetKeyState(VK_SHIFT
) & 0x8000)
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
);