winex11: Add a critical section to protect the keyboard mapping tables.

This commit is contained in:
Alexandre Julliard 2012-08-16 16:43:19 +02:00
parent c970621d90
commit 26fb3d4a69
1 changed files with 144 additions and 150 deletions

View File

@ -66,6 +66,15 @@ static WORD keyc2vkey[256], keyc2scan[256];
static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */
static CRITICAL_SECTION kbd_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &kbd_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": kbd_section") }
};
static CRITICAL_SECTION kbd_section = { &critsect_debug, -1, 0, 0, 0, 0 };
static char KEYBOARD_MapDeadKeysym(KeySym keysym);
/* Keyboard translation tables */
@ -1111,7 +1120,7 @@ static inline KeySym keycode_to_keysym( Display *display, KeyCode keycode, int i
}
/* Returns the Windows virtual key code associated with the X event <e> */
/* x11 lock must be held */
/* kbd_section must be held */
static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e)
{
KeySym keysym = 0;
@ -1229,6 +1238,8 @@ void X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
memset(modifiers, 0, sizeof(modifiers));
EnterCriticalSection( &kbd_section );
/* the minimum keycode is always greater or equal to 8, so we can
* skip the first 8 values, hence start at 1
*/
@ -1270,6 +1281,7 @@ void X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
}
}
LeaveCriticalSection( &kbd_section );
if (!changed) return;
update_key_state( keystate, VK_CONTROL, (keystate[VK_LCONTROL] | keystate[VK_RCONTROL]) & 0x80 );
@ -1341,7 +1353,6 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
if (event->type == KeyPress) update_user_time( event->time );
wine_tsx11_lock();
/* Clients should pass only KeyPress events to XmbLookupString */
if (xic && event->type == KeyPress)
{
@ -1353,7 +1364,6 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
if (Str == NULL)
{
ERR_(key)("Failed to allocate memory!\n");
wine_tsx11_unlock();
return;
}
ascii_chars = XmbLookupString(xic, event, Str, ascii_chars, &keysym, &status);
@ -1361,7 +1371,6 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
}
else
ascii_chars = XLookupString(event, buf, sizeof(buf), &keysym, NULL);
wine_tsx11_unlock();
TRACE_(key)("nbyte = %d, status %d\n", ascii_chars, status);
@ -1373,6 +1382,8 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
return;
}
EnterCriticalSection( &kbd_section );
/* If XKB extensions are used, the state mask for AltGr will use the group
index instead of the modifier mask. The group index is set in bits
13-14 of the state field in the XKeyEvent structure. So if AltGr is
@ -1398,27 +1409,26 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
if (buf != Str)
HeapFree(GetProcessHeap(), 0, Str);
wine_tsx11_lock();
vkey = EVENT_event_to_vkey(xic,event);
/* X returns keycode 0 for composed characters */
if (!vkey && ascii_chars) vkey = VK_NONAME;
wine_tsx11_unlock();
TRACE_(key)("keycode %u converted to vkey 0x%X\n",
event->keycode, vkey);
if (!vkey) return;
dwFlags = 0;
if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP;
if ( vkey & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY;
update_lock_state( hwnd, vkey, event->state, event_time );
bScan = keyc2scan[event->keycode] & 0xFF;
TRACE_(key)("bScan = 0x%02x.\n", bScan);
X11DRV_send_keyboard_input( hwnd, vkey & 0xff, bScan, dwFlags, event_time );
TRACE_(key)("keycode %u converted to vkey 0x%X scan %02x\n",
event->keycode, vkey, bScan);
if (vkey)
{
dwFlags = 0;
if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP;
if ( vkey & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY;
update_lock_state( hwnd, vkey, event->state, event_time );
X11DRV_send_keyboard_input( hwnd, vkey & 0xff, bScan, dwFlags, event_time );
}
LeaveCriticalSection( &kbd_section );
}
/**********************************************************************
@ -1427,7 +1437,7 @@ void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
* Called from X11DRV_InitKeyboard
* This routine walks through the defined keyboard layouts and selects
* whichever matches most closely.
* X11 lock must be held.
* kbd_section must be held.
*/
static void
X11DRV_KEYBOARD_DetectLayout( Display *display )
@ -1641,7 +1651,7 @@ void X11DRV_InitKeyboard( Display *display )
set_kbd_layout_preload_key();
wine_tsx11_lock();
EnterCriticalSection( &kbd_section );
XDisplayKeycodes(display, &min_keycode, &max_keycode);
if (key_mapping) XFree( key_mapping );
key_mapping = XGetKeyboardMapping(display, min_keycode,
@ -1878,7 +1888,7 @@ void X11DRV_InitKeyboard( Display *display )
keyc2scan[keyc]=scan++;
}
wine_tsx11_unlock();
LeaveCriticalSection( &kbd_section );
}
static BOOL match_x11_keyboard_layout(HKL hkl)
@ -2012,7 +2022,7 @@ SHORT CDECL X11DRV_VkKeyScanEx(WCHAR wChar, HKL hkl)
Display *display = thread_init_display();
KeyCode keycode;
KeySym keysym;
int i, index;
int index;
CHAR cChar;
SHORT ret;
@ -2031,7 +2041,6 @@ SHORT CDECL X11DRV_VkKeyScanEx(WCHAR wChar, HKL hkl)
keysym = (unsigned char)cChar; /* (!) cChar is signed */
if (keysym <= 27) keysym += 0xFF00; /* special chars : return, backspace... */
wine_tsx11_lock();
keycode = XKeysymToKeycode(display, keysym); /* keysym -> keycode */
if (!keycode)
{
@ -2039,49 +2048,41 @@ SHORT CDECL X11DRV_VkKeyScanEx(WCHAR wChar, HKL hkl)
{
ret = 0x0240 + cChar; /* 0x0200 indicates a control character */
TRACE(" ... returning ctrl char %#.2x\n", ret);
wine_tsx11_unlock();
return ret;
}
/* It didn't work ... let's try with deadchar code. */
TRACE("retrying with | 0xFE00\n");
keycode = XKeysymToKeycode(display, keysym | 0xFE00);
}
wine_tsx11_unlock();
TRACE("'%c'(%lx): got keycode %u\n", cChar, keysym, keycode);
if (!keycode) return -1;
EnterCriticalSection( &kbd_section );
/* keycode -> (keyc2vkey) vkey */
ret = keyc2vkey[keycode];
if (!keycode || !ret)
if (!ret)
{
LeaveCriticalSection( &kbd_section );
TRACE("keycode for '%c' not found, returning -1\n", cChar);
return -1;
}
index = -1;
wine_tsx11_lock();
for (i = 0; i < 4; i++) /* find shift state */
{
if (keycode_to_keysym(display, keycode, i) == keysym)
{
index = i;
break;
}
}
wine_tsx11_unlock();
for (index = 0; index < 4; index++) /* find shift state */
if (keycode_to_keysym(display, keycode, index) == keysym) break;
LeaveCriticalSection( &kbd_section );
switch (index)
{
default:
case -1:
WARN("Keysym %lx not found while parsing the keycode table\n", keysym);
return -1;
case 0: break;
case 1: ret += 0x0100; break;
case 2: ret += 0x0600; break;
case 3: ret += 0x0700; break;
default:
WARN("Keysym %lx not found while parsing the keycode table\n", keysym);
return -1;
}
/*
index : 0 adds 0x0000
@ -2100,21 +2101,20 @@ SHORT CDECL X11DRV_VkKeyScanEx(WCHAR wChar, HKL hkl)
*/
UINT CDECL X11DRV_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
{
UINT ret = 0;
int keyc;
Display *display = thread_init_display();
#define returnMVK(value) do { TRACE("returning 0x%x.\n",value); return value; } while(0)
TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
if (!match_x11_keyboard_layout(hkl))
FIXME("keyboard layout %p is not supported\n", hkl);
EnterCriticalSection( &kbd_section );
switch(wMapType)
{
case MAPVK_VK_TO_VSC: /* vkey-code to scan-code */
case MAPVK_VK_TO_VSC_EX:
{
int keyc;
switch (wCode)
{
case VK_SHIFT: wCode = VK_LSHIFT; break;
@ -2124,127 +2124,115 @@ UINT CDECL X11DRV_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
/* let's do vkey -> keycode -> scan */
for (keyc = min_keycode; keyc <= max_keycode; keyc++)
if ((keyc2vkey[keyc] & 0xFF) == wCode) break;
if (keyc > max_keycode)
{
TRACE("returning no scan-code.\n");
return 0;
if ((keyc2vkey[keyc] & 0xFF) == wCode)
{
ret = keyc2scan[keyc] & 0xFF;
break;
}
}
returnMVK (keyc2scan[keyc] & 0xFF);
}
break;
case MAPVK_VSC_TO_VK: /* scan-code to vkey-code */
case MAPVK_VSC_TO_VK_EX:
{
int keyc;
UINT vkey = 0;
/* let's do scan -> keycode -> vkey */
for (keyc = min_keycode; keyc <= max_keycode; keyc++)
if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
{
vkey = keyc2vkey[keyc] & 0xFF;
ret = keyc2vkey[keyc] & 0xFF;
/* Only stop if it's not a numpad vkey; otherwise keep
looking for a potential better vkey. */
if (vkey && (vkey < VK_NUMPAD0 || VK_DIVIDE < vkey))
if (ret && (ret < VK_NUMPAD0 || VK_DIVIDE < ret))
break;
}
if (vkey == 0)
{
TRACE("returning no vkey-code.\n");
return 0;
}
if (wMapType == MAPVK_VSC_TO_VK)
switch (vkey)
switch (ret)
{
case VK_LSHIFT:
case VK_RSHIFT:
vkey = VK_SHIFT; break;
ret = VK_SHIFT; break;
case VK_LCONTROL:
case VK_RCONTROL:
vkey = VK_CONTROL; break;
ret = VK_CONTROL; break;
case VK_LMENU:
case VK_RMENU:
vkey = VK_MENU; break;
ret = VK_MENU; break;
}
returnMVK (vkey);
break;
case MAPVK_VK_TO_CHAR: /* vkey-code to unshifted ANSI code */
{
/* we still don't know what "unshifted" means. in windows VK_W (0x57)
* returns 0x57, which is uppercase 'W'. So we have to return the uppercase
* key.. Looks like something is wrong with the MS docs?
* This is only true for letters, for example VK_0 returns '0' not ')'.
* - hence we use the lock mask to ensure this happens.
*/
/* let's do vkey -> keycode -> (XLookupString) ansi char */
XKeyEvent e;
KeySym keysym;
int len;
char s[10];
e.display = display;
e.state = 0;
e.keycode = 0;
e.type = KeyPress;
/* We exit on the first keycode found, to speed up the thing. */
for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
{ /* Find a keycode that could have generated this virtual key */
if ((keyc2vkey[keyc] & 0xFF) == wCode)
{ /* We filter the extended bit, we don't know it */
e.keycode = keyc; /* Store it temporarily */
if ((EVENT_event_to_vkey(0,&e) & 0xFF) != wCode) {
e.keycode = 0; /* Wrong one (ex: because of the NumLock
state), so set it to 0, we'll find another one */
}
}
}
if ((wCode>=VK_NUMPAD0) && (wCode<=VK_NUMPAD9))
e.keycode = XKeysymToKeycode(e.display, wCode-VK_NUMPAD0+XK_KP_0);
/* Windows always generates VK_DECIMAL for Del/. on keypad while some
* X11 keyboard layouts generate XK_KP_Separator instead of XK_KP_Decimal
* in order to produce a locale dependent numeric separator.
*/
if (wCode == VK_DECIMAL || wCode == VK_SEPARATOR)
{
e.keycode = XKeysymToKeycode(e.display, XK_KP_Separator);
if (!e.keycode)
e.keycode = XKeysymToKeycode(e.display, XK_KP_Decimal);
}
if (!e.keycode)
{
WARN("Unknown virtual key %X !!!\n", wCode);
break;
}
TRACE("Found keycode %u\n",e.keycode);
len = XLookupString(&e, s, sizeof(s), &keysym, NULL);
if (len)
{
WCHAR wch;
if (MultiByteToWideChar(CP_UNIXCP, 0, s, len, &wch, 1)) ret = toupperW(wch);
}
break;
}
case MAPVK_VK_TO_CHAR: /* vkey-code to unshifted ANSI code */
{
/* we still don't know what "unshifted" means. in windows VK_W (0x57)
* returns 0x57, which is uppercase 'W'. So we have to return the uppercase
* key.. Looks like something is wrong with the MS docs?
* This is only true for letters, for example VK_0 returns '0' not ')'.
* - hence we use the lock mask to ensure this happens.
*/
/* let's do vkey -> keycode -> (XLookupString) ansi char */
XKeyEvent e;
KeySym keysym;
int keyc, len;
char s[10];
e.display = display;
e.state = 0;
e.keycode = 0;
e.type = KeyPress;
default: /* reserved */
FIXME("Unknown wMapType %d !\n", wMapType);
break;
}
wine_tsx11_lock();
/* We exit on the first keycode found, to speed up the thing. */
for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
{ /* Find a keycode that could have generated this virtual key */
if ((keyc2vkey[keyc] & 0xFF) == wCode)
{ /* We filter the extended bit, we don't know it */
e.keycode = keyc; /* Store it temporarily */
if ((EVENT_event_to_vkey(0,&e) & 0xFF) != wCode) {
e.keycode = 0; /* Wrong one (ex: because of the NumLock
state), so set it to 0, we'll find another one */
}
}
}
if ((wCode>=VK_NUMPAD0) && (wCode<=VK_NUMPAD9))
e.keycode = XKeysymToKeycode(e.display, wCode-VK_NUMPAD0+XK_KP_0);
/* Windows always generates VK_DECIMAL for Del/. on keypad while some
* X11 keyboard layouts generate XK_KP_Separator instead of XK_KP_Decimal
* in order to produce a locale dependent numeric separator.
*/
if (wCode == VK_DECIMAL || wCode == VK_SEPARATOR)
{
e.keycode = XKeysymToKeycode(e.display, XK_KP_Separator);
if (!e.keycode)
e.keycode = XKeysymToKeycode(e.display, XK_KP_Decimal);
}
if (!e.keycode)
{
WARN("Unknown virtual key %X !!!\n", wCode);
wine_tsx11_unlock();
return 0; /* whatever */
}
TRACE("Found keycode %u\n",e.keycode);
len = XLookupString(&e, s, sizeof(s), &keysym, NULL);
wine_tsx11_unlock();
if (len)
{
WCHAR wch;
if (MultiByteToWideChar(CP_UNIXCP, 0, s, len, &wch, 1))
returnMVK(toupperW(wch));
}
TRACE("returning no ANSI.\n");
return 0;
}
default: /* reserved */
FIXME("Unknown wMapType %d !\n", wMapType);
return 0;
}
return 0;
LeaveCriticalSection( &kbd_section );
TRACE( "returning 0x%x.\n", ret );
return ret;
}
/***********************************************************************
@ -2319,6 +2307,8 @@ INT CDECL X11DRV_GetKeyNameText(LONG lParam, LPWSTR lpBuffer, INT nSize)
/* let's do scancode -> keycode -> keysym -> String */
EnterCriticalSection( &kbd_section );
for (keyi=min_keycode; keyi<=max_keycode; keyi++)
if ((keyc2scan[keyi]) == scanCode)
break;
@ -2326,17 +2316,16 @@ INT CDECL X11DRV_GetKeyNameText(LONG lParam, LPWSTR lpBuffer, INT nSize)
{
INT rc;
wine_tsx11_lock();
keyc = (KeyCode) keyi;
keys = keycode_to_keysym(display, keyc, 0);
name = XKeysymToString(keys);
wine_tsx11_unlock();
if (name && (vkey == VK_SHIFT || vkey == VK_CONTROL || vkey == VK_MENU))
{
char* idx = strrchr(name, '_');
if (idx && (strcasecmp(idx, "_r") == 0 || strcasecmp(idx, "_l") == 0))
{
LeaveCriticalSection( &kbd_section );
TRACE("found scan=%04x keyc=%u keysym=%lx modified_string=%s\n",
scanCode, keyc, keys, debugstr_an(name,idx-name));
rc = MultiByteToWideChar(CP_UNIXCP, 0, name, idx-name+1, lpBuffer, nSize);
@ -2348,6 +2337,7 @@ INT CDECL X11DRV_GetKeyNameText(LONG lParam, LPWSTR lpBuffer, INT nSize)
if (name)
{
LeaveCriticalSection( &kbd_section );
TRACE("found scan=%04x keyc=%u keysym=%04x vkey=%04x string=%s\n",
scanCode, keyc, (int)keys, vkey, debugstr_a(name));
rc = MultiByteToWideChar(CP_UNIXCP, 0, name, -1, lpBuffer, nSize);
@ -2359,6 +2349,7 @@ INT CDECL X11DRV_GetKeyNameText(LONG lParam, LPWSTR lpBuffer, INT nSize)
/* Finally issue WARN for unknown keys */
LeaveCriticalSection( &kbd_section );
WARN("(%08x,%p,%d): unsupported key, vkey=%04X, ansi=%04x\n",lParam,lpBuffer,nSize,vkey,ansi);
*lpBuffer = 0;
return 0;
@ -2504,6 +2495,8 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
e.window = X11DRV_get_whole_window( focus );
xic = X11DRV_get_ic( focus );
EnterCriticalSection( &kbd_section );
if (lpKeyState[VK_SHIFT] & 0x80)
{
TRACE_(key)("ShiftMask = %04x\n", ShiftMask);
@ -2531,7 +2524,7 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
TRACE_(key)("(%04X, %04X) : faked state = 0x%04x\n",
virtKey, scanCode, e.state);
wine_tsx11_lock();
/* We exit on the first keycode found, to speed up the thing. */
for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
{ /* Find a keycode that could have generated this virtual key */
@ -2565,7 +2558,7 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
if (!e.keycode && virtKey != VK_NONAME)
{
WARN_(key)("Unknown virtual key %X !!!\n", virtKey);
wine_tsx11_unlock();
LeaveCriticalSection( &kbd_section );
return 0;
}
else TRACE_(key)("Found keycode %u\n",e.keycode);
@ -2586,7 +2579,7 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
if (lpChar == NULL)
{
ERR_(key)("Failed to allocate memory!\n");
wine_tsx11_unlock();
LeaveCriticalSection( &kbd_section );
return 0;
}
ret = XmbLookupString(xic, &e, lpChar, ret, &keysym, &status);
@ -2594,7 +2587,6 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
}
else
ret = XLookupString(&e, buf, sizeof(buf), &keysym, NULL);
wine_tsx11_unlock();
TRACE_(key)("nbyte = %d, status 0x%x\n", ret, status);
@ -2731,6 +2723,8 @@ found:
if (buf != lpChar)
HeapFree(GetProcessHeap(), 0, lpChar);
LeaveCriticalSection( &kbd_section );
/* Null-terminate the buffer, if there's room. MSDN clearly states that the
caller must not assume this is done, but some programs (e.g. Audiosurf) do. */
if (1 <= ret && ret < bufW_size)