Requesting testing for the hotkey implementation

Aaron Slunt tonglebeak at gmail.com
Tue Oct 10 18:11:04 CDT 2006


I'm attaching the patch, first started by Robert Reif, then finished off 
by Vincent Povirk, and cleaned up by myself to apply to the current GIT 
tree.

I'm asking devs/users to please test this patch out. It implements 
Register/UnregisterHotkey in wine. I have yet to come across any issues 
with it. The primary concern for this patch would be  to see what 
happens when two separate programs, or two instances of the same 
program, try to register a hotkey that is already in use.

Please let me know how this turns out. It would be really great to 
finally have this in wine, but testing needs to happen first.

Thanks.

----------------------------------------------------------------------

Index: dlls/user/driver.c
===================================================================
RCS file: /home/wine/wine/dlls/user/driver.c,v
retrieving revision 1.4
diff -u -r1.4 driver.c
--- dlls/user/driver.c    3 Apr 2006 19:46:56 -0000    1.4
+++ dlls/user/driver.c    7 May 2006 20:15:34 -0000
@@ -76,6 +76,8 @@
 
         GET_USER_FUNC(ActivateKeyboardLayout);
         GET_USER_FUNC(Beep);
+        GET_USER_FUNC(RegisterHotKey);
+        GET_USER_FUNC(UnregisterHotKey);
         GET_USER_FUNC(GetAsyncKeyState);
         GET_USER_FUNC(GetKeyNameText);
         GET_USER_FUNC(GetKeyboardLayout);
@@ -216,6 +218,16 @@
     return -1;
 }
 
+static BOOL nulldrv_RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
+{
+    return FALSE;
+}
+
+static BOOL nulldrv_UnregisterHotKey(HWND hwnd,INT id)
+{
+    return FALSE;
+}
+
 static void nulldrv_SetCursor( struct tagCURSORICONINFO *info )
 {
 }
@@ -421,6 +433,8 @@
     nulldrv_ToUnicodeEx,
     nulldrv_UnloadKeyboardLayout,
     nulldrv_VkKeyScanEx,
+    nulldrv_RegisterHotKey,
+    nulldrv_UnregisterHotKey,
     /* mouse functions */
     nulldrv_SetCursor,
     nulldrv_GetCursorPos,
@@ -538,6 +552,16 @@
     return load_driver()->pVkKeyScanEx( ch, layout );
 }
 
+static BOOL loaderdrv_RegisterHotKey(HWND hwnd,INT id,UINT 
modifiers,UINT vk)
+{
+    return load_driver()->pRegisterHotKey( hwnd, id, modifiers, vk );
+}
+
+static BOOL loaderdrv_UnregisterHotKey(HWND hwnd,INT id)
+{
+    return load_driver()->pUnregisterHotKey( hwnd, id );
+}
+
 static void loaderdrv_SetCursor( struct tagCURSORICONINFO *info )
 {
     load_driver()->pSetCursor( info );
@@ -737,6 +761,8 @@
     loaderdrv_ToUnicodeEx,
     loaderdrv_UnloadKeyboardLayout,
     loaderdrv_VkKeyScanEx,
+    loaderdrv_RegisterHotKey,
+    loaderdrv_UnregisterHotKey,
     /* mouse functions */
     loaderdrv_SetCursor,
     loaderdrv_GetCursorPos,
Index: dlls/user/input.c
===================================================================
RCS file: /home/wine/wine/dlls/user/input.c,v
retrieving revision 1.10
diff -u -r1.10 input.c
--- dlls/user/input.c    27 Mar 2006 20:51:17 -0000    1.10
+++ dlls/user/input.c    7 May 2006 20:15:35 -0000
@@ -653,8 +653,10 @@
  */
 BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
 {
-    FIXME_(keyboard)("(%p,%d,0x%08x,%d): stub\n",hwnd,id,modifiers,vk);
-    return TRUE;
+    TRACE_(keyboard)("(%p,0x%x,0x%08x,0x%08x)\n",hwnd,id,modifiers,vk);
+    if (USER_Driver->pRegisterHotKey)
+    return USER_Driver->pRegisterHotKey( hwnd, id, modifiers, vk );
+    return FALSE;
 }
 
 /***********************************************************************
@@ -662,8 +664,10 @@
  */
 BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id)
 {
-    FIXME_(keyboard)("(%p,%d): stub\n",hwnd,id);
-    return TRUE;
+    TRACE_(keyboard)("(%p,%d)\n",hwnd,id);
+    if (USER_Driver->pUnregisterHotKey)
+    return USER_Driver->pUnregisterHotKey( hwnd, id );
+    return FALSE;
 }
 
 /***********************************************************************
Index: dlls/user/user_private.h
===================================================================
RCS file: /home/wine/wine/dlls/user/user_private.h,v
retrieving revision 1.20
diff -u -r1.20 user_private.h
--- dlls/user/user_private.h    10 Aug 2005 09:56:23 -0000    1.20
+++ dlls/user/user_private.h    7 May 2006 20:15:35 -0000
@@ -114,6 +114,8 @@
     INT    (*pToUnicodeEx)(UINT, UINT, LPBYTE, LPWSTR, int, UINT, HKL);
     BOOL   (*pUnloadKeyboardLayout)(HKL);
     SHORT  (*pVkKeyScanEx)(WCHAR, HKL);
+    BOOL   (*pRegisterHotKey)(HWND, INT, UINT, UINT);
+    BOOL   (*pUnregisterHotKey)(HWND, INT);
     /* mouse functions */
     void   (*pSetCursor)(struct tagCURSORICONINFO *);
     BOOL   (*pGetCursorPos)(LPPOINT);
Index: dlls/x11drv/keyboard.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/keyboard.c,v
retrieving revision 1.90
diff -u -r1.90 keyboard.c
--- dlls/x11drv/keyboard.c    20 Apr 2006 09:41:15 -0000    1.90
+++ dlls/x11drv/keyboard.c    7 May 2006 20:15:38 -0000
@@ -89,7 +89,7 @@
 static int min_keycode, max_keycode, keysyms_per_keycode;
 static WORD keyc2vkey[256], keyc2scan[256];
 
-static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
+static int NumLockMask, ScrollLockMask, AnyLockMask, AltGrMask, 
MetaMask, SuperMask; /* mask in the XKeyEvent state */
 static int kcControl, kcAlt, kcShift, kcNumLock, kcCapsLock; /* keycodes */
 
 static char KEYBOARD_MapDeadKeysym(KeySym keysym);
@@ -1075,6 +1075,91 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
 };
 
+/***********************************************************************
+ *                 HotKey
+ */
+typedef struct GRABKEY_INFO {
+    HWND hWnd;            /* windows window to receive hot-key 
notification */
+    INT id;            /* windows identifier of hot key */
+    UINT fsModifiers;        /* windows key-modifier flag */
+    UINT vk;            /* windows virtual-key code */
+    int x_keycode;        /* X keycode */
+    unsigned int x_modifiers;    /* X modifiers */
+} GRABKEY_INFO;
+
+static GRABKEY_INFO * pKeys = 0;
+static int nKeys = 0;
+static int nKeysMax = 0;
+
+static int add_grab(HWND hwnd, INT id, UINT modifiers, UINT vk, 
unsigned int x_modifiers, int x_keycode)
+{
+    
TRACE("(%p,%x,%x,%x,%x,%x)\n",hwnd,id,modifiers,vk,x_modifiers,x_keycode);
+
+    if ( (pKeys == 0) || (nKeys == nKeysMax) ) {
+    GRABKEY_INFO * temp;
+    if (nKeys)
+        temp = (GRABKEY_INFO *)HeapReAlloc(GetProcessHeap(),0, pKeys, 
(nKeysMax + 10) * sizeof(GRABKEY_INFO));
+    else
+        temp = (GRABKEY_INFO *)HeapAlloc(GetProcessHeap(),0, 10 * 
sizeof(GRABKEY_INFO));
+    if (temp) {
+        pKeys = temp;
+        nKeysMax += 10;
+    } else
+        return False;
+    }
+
+    pKeys[nKeys].hWnd = hwnd;
+    pKeys[nKeys].id = id;
+    pKeys[nKeys].fsModifiers = modifiers;
+    pKeys[nKeys].vk = vk;
+    pKeys[nKeys].x_keycode = x_keycode;
+    pKeys[nKeys].x_modifiers = x_modifiers;
+    nKeys++;
+    return True;
+}
+
+static GRABKEY_INFO * find_grab(INT id)
+{
+    int i;
+    TRACE("(%x)\n", id);
+    for (i = 0; i < nKeys; i++) {
+    if (pKeys[i].id == id)
+        return &pKeys[i];
+    }
+    return 0;
+}
+
+static GRABKEY_INFO * has_grab(XKeyEvent *event)
+{
+    int i;
+    int modifiers = event->state & ~AnyLockMask;
+    TRACE("(%p) event->keycode=%d\n",event,event->keycode);
+    for (i = 0; i < nKeys; i++) {
+    if ((pKeys[i].x_keycode == event->keycode) && (pKeys[i].x_modifiers 
== modifiers))
+        return &pKeys[i];
+    }
+    return 0;
+}
+
+static int remove_grab(INT id)
+{
+    int i,j;
+    TRACE("(%x)\n", id);
+    for (i = 0; i < nKeys; i++) {
+    if (pKeys[i].id == id) {
+        for (j = i; j < (nKeys - 1); j++)
+        pKeys[j] = pKeys[j + 1];
+        nKeys--;
+        if (nKeys == 0) {
+        HeapFree(GetProcessHeap(),0, pKeys);
+        pKeys = 0;
+        nKeysMax = 0;
+        }
+        return True;
+    }
+    }
+    return False;
+}
 
 /* Returns the Windows virtual key code associated with the X event <e> */
 /* x11 lock must be held */
@@ -1325,6 +1410,15 @@
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
         event->type, event->window, event->state, event->keycode);
 
+    if (event->type == KeyPress) {
+    GRABKEY_INFO * hotkey = has_grab(event);
+
+    if (hotkey) {
+        PostMessageA(hotkey->hWnd, WM_HOTKEY, hotkey->id, 
MAKEWORD(hotkey->fsModifiers, hotkey->vk));
+        return;
+    }
+    }
+
     wine_tsx11_lock();
     if (xic)
         ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), 
&keysym, &status);
@@ -1576,15 +1670,36 @@
             {
         int k;
 
-        for (k = 0; k < keysyms_per_keycode; k += 1)
-                    if (XKeycodeToKeysym(display, *kcp, k) == XK_Num_Lock)
-            {
-                        NumLockMask = 1 << i;
+        for (k = 0; k < keysyms_per_keycode; k += 1) {
+            KeySym sym = XKeycodeToKeysym(display, *kcp, k);
+            switch (sym) {
+                case XK_Num_Lock:
+                        NumLockMask |= 1 << i;
                         TRACE_(key)("NumLockMask is %x\n", NumLockMask);
+                        break;
+                       
+                case XK_Scroll_Lock:
+                        ScrollLockMask |= 1 << i;
+                        TRACE_(key)("ScrollLockMask is %x\n", 
ScrollLockMask);
+                        break;
+                       
+                case XK_Meta_L: case XK_Meta_R:
+                        MetaMask |= 1 << i;
+                        TRACE_(key)("MetaMask is %x\n", MetaMask);
+                        break;
+                       
+                case XK_Super_L: case XK_Super_R:
+                        SuperMask |= 1 << i;
+                        TRACE_(key)("SuperMask is %x\n", SuperMask);
+                        break;
+
+                default: break;
             }
+        }
             }
     }
     XFreeModifiermap(mmp);
+    AnyLockMask = LockMask|NumLockMask|ScrollLockMask;
 
     /* Detect the keyboard layout */
     X11DRV_KEYBOARD_DetectLayout();
@@ -2521,3 +2636,98 @@
     XBell(thread_display(), 0);
     wine_tsx11_unlock();
 }
+
+/***********************************************************************
+ *        RegisterHotKey (X11DRV.@)
+ */
+BOOL X11DRV_RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
+{
+    Display *display;
+    Window window;
+    BOOL ret;
+    int x_modifiers = 0, grab_mask, ungrab_mask;
+    int x_keycode = 0;
+    int err;
+    int keyc;
+
+    TRACE("(%p,0x%x,0x%08x,0x%08x)\n",hwnd,id,modifiers,vk);
+   
+    display = thread_display();
+    window = X11DRV_get_whole_window(hwnd);
+
+    if (modifiers & MOD_ALT)
+    x_modifiers |= MetaMask;
+    if (modifiers & MOD_CONTROL)
+    x_modifiers |= ControlMask;
+    if (modifiers & MOD_SHIFT)
+    x_modifiers |= ShiftMask;
+    if (modifiers & MOD_WIN)
+    x_modifiers |= SuperMask;
+
+    for (keyc=min_keycode; keyc<=max_keycode; keyc++) {
+    if ((keyc2vkey[keyc] & 0xFF) == vk) {
+        TRACE("keycode found\n");
+        x_keycode = keyc;
+        break;
+    }
+    }
+
+    if (keyc > max_keycode) {
+    TRACE("keycode not found\n");
+    return FALSE;
+    }
+
+    /* We need to register every combination of the locks to ignore them */
+    wine_tsx11_lock();
+    grab_mask = AnyLockMask;
+    do {
+        err = XGrabKey(display, x_keycode, x_modifiers|grab_mask, 
DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
+        if (err == 0) break;
+        grab_mask = (grab_mask-1) & AnyLockMask;
+    } while (grab_mask != AnyLockMask);
+   
+    if (err == 0) {
+    FIXME("failed to grab keycode=0x%04x modifiers=0x%04x", x_keycode, 
x_modifiers);
+    /* FIXME: set proper error value */
+    SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
+    /* unregister anything we succeeded in grabbing */
+    ungrab_mask = AnyLockMask;
+    while (ungrab_mask != grab_mask) {
+        XUngrabKey(display, x_keycode, x_modifiers|ungrab_mask, 
DefaultRootWindow(display));
+        ungrab_mask = (ungrab_mask-1) & AnyLockMask;
+    }
+    ret = FALSE;   
+    } else {
+    add_grab(hwnd, id, modifiers, vk, x_modifiers, x_keycode);
+    ret = TRUE;
+    }
+    wine_tsx11_unlock();
+
+    return ret;
+}
+
+/***********************************************************************
+ *        UnregisterHotKey (X11DRV.@)
+ */
+BOOL X11DRV_UnregisterHotKey(HWND hwnd,INT id)
+{
+    Display *display = thread_display();
+    Window window = X11DRV_get_whole_window(hwnd);
+    GRABKEY_INFO * grab = find_grab(id);
+    int ungrab_mask;
+   
+    TRACE("(%lx,%d)\n",(DWORD)hwnd,id);
+
+    if (grab) {
+        wine_tsx11_lock();
+        ungrab_mask = AnyLockMask;
+        do {
+        XUngrabKey(display, grab->x_keycode, 
grab->x_modifiers|ungrab_mask, window);
+        ungrab_mask = (ungrab_mask-1) & AnyLockMask;
+    } while (ungrab_mask != AnyLockMask);
+    wine_tsx11_unlock();
+    remove_grab(id);
+    return TRUE;
+    }
+    return FALSE;
+}
Index: dlls/x11drv/winex11.drv.spec
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/winex11.drv.spec,v
retrieving revision 1.5
diff -u -r1.5 winex11.drv.spec
--- dlls/x11drv/winex11.drv.spec    27 Mar 2006 20:51:24 -0000    1.5
+++ dlls/x11drv/winex11.drv.spec    7 May 2006 20:15:38 -0000
@@ -76,6 +76,8 @@
 @ cdecl ToUnicodeEx(long long ptr ptr long long long) X11DRV_ToUnicodeEx
 @ cdecl UnloadKeyboardLayout(long) X11DRV_UnloadKeyboardLayout
 @ cdecl VkKeyScanEx(long long) X11DRV_VkKeyScanEx
+@ cdecl RegisterHotKey(long long long long) X11DRV_RegisterHotKey
+@ cdecl UnregisterHotKey(long long) X11DRV_UnregisterHotKey
 @ cdecl SetCursor(ptr) X11DRV_SetCursor
 @ cdecl GetCursorPos(ptr) X11DRV_GetCursorPos
 @ cdecl SetCursorPos(long long) X11DRV_SetCursorPos
Index: include/winbase.h
===================================================================
RCS file: /home/wine/wine/include/winbase.h,v
retrieving revision 1.253
diff -u -r1.253 winbase.h
--- include/winbase.h    16 Mar 2006 20:41:46 -0000    1.253
+++ include/winbase.h    7 May 2006 20:15:40 -0000
@@ -1844,6 +1844,7 @@
 HANDLE      WINAPI RegisterEventSourceA(LPCSTR,LPCSTR);
 HANDLE      WINAPI RegisterEventSourceW(LPCWSTR,LPCWSTR);
 #define     RegisterEventSource WINELIB_NAME_AW(RegisterEventSource)
+BOOL        WINAPI RegisterHotKey(HWND,INT,UINT,UINT);
 BOOL        WINAPI 
RegisterWaitForSingleObject(PHANDLE,HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
 HANDLE      WINAPI 
RegisterWaitForSingleObjectEx(HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
 VOID        WINAPI ReleaseActCtx(HANDLE);
@@ -1971,6 +1972,7 @@
 BOOL        WINAPI UnlockFileEx(HANDLE,DWORD,DWORD,DWORD,LPOVERLAPPED);
 #define     UnlockSegment(handle) GlobalUnfix((HANDLE)(handle))
 BOOL        WINAPI UnmapViewOfFile(LPVOID);
+BOOL        WINAPI UnregisterHotKey(HWND,INT);
 BOOL        WINAPI UnregisterWait(HANDLE);
 BOOL        WINAPI UnregisterWaitEx(HANDLE,HANDLE);
 BOOL        WINAPI UpdateResourceA(HANDLE,LPCSTR,LPCSTR,WORD,LPVOID,DWORD);

-------------- next part --------------
? 01-hotkey.diff
? evil-window-size-hack
? hotkeys.patch
? wa-hack
? wa_hack_complete
? dlls/commdlg/800.bmp
? dlls/commdlg/Makefile
? dlls/commdlg/cdrom.ico
? dlls/commdlg/floppy.ico
? dlls/commdlg/folder.ico
? dlls/commdlg/folder2.ico
? dlls/commdlg/fontpics.bmp
Index: dlls/user/driver.c
===================================================================
RCS file: /home/wine/wine/dlls/user/driver.c,v
retrieving revision 1.4
diff -u -r1.4 driver.c
--- dlls/user/driver.c	3 Apr 2006 19:46:56 -0000	1.4
+++ dlls/user/driver.c	7 May 2006 20:15:34 -0000
@@ -76,6 +76,8 @@
 
         GET_USER_FUNC(ActivateKeyboardLayout);
         GET_USER_FUNC(Beep);
+        GET_USER_FUNC(RegisterHotKey);
+        GET_USER_FUNC(UnregisterHotKey);
         GET_USER_FUNC(GetAsyncKeyState);
         GET_USER_FUNC(GetKeyNameText);
         GET_USER_FUNC(GetKeyboardLayout);
@@ -216,6 +218,16 @@
     return -1;
 }
 
+static BOOL nulldrv_RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
+{
+    return FALSE;
+}
+
+static BOOL nulldrv_UnregisterHotKey(HWND hwnd,INT id)
+{
+    return FALSE;
+}
+
 static void nulldrv_SetCursor( struct tagCURSORICONINFO *info )
 {
 }
@@ -421,6 +433,8 @@
     nulldrv_ToUnicodeEx,
     nulldrv_UnloadKeyboardLayout,
     nulldrv_VkKeyScanEx,
+    nulldrv_RegisterHotKey,
+    nulldrv_UnregisterHotKey,
     /* mouse functions */
     nulldrv_SetCursor,
     nulldrv_GetCursorPos,
@@ -538,6 +552,16 @@
     return load_driver()->pVkKeyScanEx( ch, layout );
 }
 
+static BOOL loaderdrv_RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
+{
+    return load_driver()->pRegisterHotKey( hwnd, id, modifiers, vk );
+}
+
+static BOOL loaderdrv_UnregisterHotKey(HWND hwnd,INT id)
+{
+    return load_driver()->pUnregisterHotKey( hwnd, id );
+}
+
 static void loaderdrv_SetCursor( struct tagCURSORICONINFO *info )
 {
     load_driver()->pSetCursor( info );
@@ -737,6 +761,8 @@
     loaderdrv_ToUnicodeEx,
     loaderdrv_UnloadKeyboardLayout,
     loaderdrv_VkKeyScanEx,
+    loaderdrv_RegisterHotKey,
+    loaderdrv_UnregisterHotKey,
     /* mouse functions */
     loaderdrv_SetCursor,
     loaderdrv_GetCursorPos,
Index: dlls/user/input.c
===================================================================
RCS file: /home/wine/wine/dlls/user/input.c,v
retrieving revision 1.10
diff -u -r1.10 input.c
--- dlls/user/input.c	27 Mar 2006 20:51:17 -0000	1.10
+++ dlls/user/input.c	7 May 2006 20:15:35 -0000
@@ -653,8 +653,10 @@
  */
 BOOL WINAPI RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
 {
-    FIXME_(keyboard)("(%p,%d,0x%08x,%d): stub\n",hwnd,id,modifiers,vk);
-    return TRUE;
+    TRACE_(keyboard)("(%p,0x%x,0x%08x,0x%08x)\n",hwnd,id,modifiers,vk);
+    if (USER_Driver->pRegisterHotKey) 
+	return USER_Driver->pRegisterHotKey( hwnd, id, modifiers, vk );
+    return FALSE;
 }
 
 /***********************************************************************
@@ -662,8 +664,10 @@
  */
 BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id)
 {
-    FIXME_(keyboard)("(%p,%d): stub\n",hwnd,id);
-    return TRUE;
+    TRACE_(keyboard)("(%p,%d)\n",hwnd,id);
+    if (USER_Driver->pUnregisterHotKey) 
+	return USER_Driver->pUnregisterHotKey( hwnd, id );
+    return FALSE;
 }
 
 /***********************************************************************
Index: dlls/user/user_private.h
===================================================================
RCS file: /home/wine/wine/dlls/user/user_private.h,v
retrieving revision 1.20
diff -u -r1.20 user_private.h
--- dlls/user/user_private.h	10 Aug 2005 09:56:23 -0000	1.20
+++ dlls/user/user_private.h	7 May 2006 20:15:35 -0000
@@ -114,6 +114,8 @@
     INT    (*pToUnicodeEx)(UINT, UINT, LPBYTE, LPWSTR, int, UINT, HKL);
     BOOL   (*pUnloadKeyboardLayout)(HKL);
     SHORT  (*pVkKeyScanEx)(WCHAR, HKL);
+    BOOL   (*pRegisterHotKey)(HWND, INT, UINT, UINT);
+    BOOL   (*pUnregisterHotKey)(HWND, INT);
     /* mouse functions */
     void   (*pSetCursor)(struct tagCURSORICONINFO *);
     BOOL   (*pGetCursorPos)(LPPOINT);
Index: dlls/x11drv/keyboard.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/keyboard.c,v
retrieving revision 1.90
diff -u -r1.90 keyboard.c
--- dlls/x11drv/keyboard.c	20 Apr 2006 09:41:15 -0000	1.90
+++ dlls/x11drv/keyboard.c	7 May 2006 20:15:38 -0000
@@ -89,7 +89,7 @@
 static int min_keycode, max_keycode, keysyms_per_keycode;
 static WORD keyc2vkey[256], keyc2scan[256];
 
-static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
+static int NumLockMask, ScrollLockMask, AnyLockMask, AltGrMask, MetaMask, SuperMask; /* mask in the XKeyEvent state */
 static int kcControl, kcAlt, kcShift, kcNumLock, kcCapsLock; /* keycodes */
 
 static char KEYBOARD_MapDeadKeysym(KeySym keysym);
@@ -1075,6 +1075,91 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
 };
 
+/***********************************************************************
+ * 				HotKey
+ */
+typedef struct GRABKEY_INFO {
+    HWND hWnd;			/* windows window to receive hot-key notification */
+    INT id;			/* windows identifier of hot key */
+    UINT fsModifiers;		/* windows key-modifier flag */
+    UINT vk;			/* windows virtual-key code */
+    int x_keycode;		/* X keycode */
+    unsigned int x_modifiers;	/* X modifiers */
+} GRABKEY_INFO;
+
+static GRABKEY_INFO * pKeys = 0;
+static int nKeys = 0;
+static int nKeysMax = 0;
+
+static int add_grab(HWND hwnd, INT id, UINT modifiers, UINT vk, unsigned int x_modifiers, int x_keycode)
+{
+    TRACE("(%p,%x,%x,%x,%x,%x)\n",hwnd,id,modifiers,vk,x_modifiers,x_keycode);
+
+    if ( (pKeys == 0) || (nKeys == nKeysMax) ) {
+	GRABKEY_INFO * temp;
+	if (nKeys)
+	    temp = (GRABKEY_INFO *)HeapReAlloc(GetProcessHeap(),0, pKeys, (nKeysMax + 10) * sizeof(GRABKEY_INFO));
+	else
+	    temp = (GRABKEY_INFO *)HeapAlloc(GetProcessHeap(),0, 10 * sizeof(GRABKEY_INFO));
+	if (temp) {
+	    pKeys = temp;
+	    nKeysMax += 10;
+	} else
+	    return False;
+    }
+
+    pKeys[nKeys].hWnd = hwnd;
+    pKeys[nKeys].id = id;
+    pKeys[nKeys].fsModifiers = modifiers;
+    pKeys[nKeys].vk = vk;
+    pKeys[nKeys].x_keycode = x_keycode;
+    pKeys[nKeys].x_modifiers = x_modifiers;
+    nKeys++;
+    return True;
+}
+
+static GRABKEY_INFO * find_grab(INT id)
+{
+    int i;
+    TRACE("(%x)\n", id);
+    for (i = 0; i < nKeys; i++) {
+	if (pKeys[i].id == id)
+	    return &pKeys[i];
+    }
+    return 0;
+}
+
+static GRABKEY_INFO * has_grab(XKeyEvent *event)
+{
+    int i;
+    int modifiers = event->state & ~AnyLockMask;
+    TRACE("(%p) event->keycode=%d\n",event,event->keycode);
+    for (i = 0; i < nKeys; i++) {
+	if ((pKeys[i].x_keycode == event->keycode) && (pKeys[i].x_modifiers == modifiers))
+	    return &pKeys[i];
+    }
+    return 0;
+}
+
+static int remove_grab(INT id)
+{
+    int i,j;
+    TRACE("(%x)\n", id);
+    for (i = 0; i < nKeys; i++) {
+	if (pKeys[i].id == id) {
+	    for (j = i; j < (nKeys - 1); j++)
+		pKeys[j] = pKeys[j + 1];
+	    nKeys--;
+	    if (nKeys == 0) {
+		HeapFree(GetProcessHeap(),0, pKeys);
+		pKeys = 0;
+		nKeysMax = 0;
+	    }
+	    return True;
+	}
+    }
+    return False;
+}
 
 /* Returns the Windows virtual key code associated with the X event <e> */
 /* x11 lock must be held */
@@ -1325,6 +1410,15 @@
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
 		event->type, event->window, event->state, event->keycode);
 
+    if (event->type == KeyPress) {
+	GRABKEY_INFO * hotkey = has_grab(event);
+
+	if (hotkey) {
+	    PostMessageA(hotkey->hWnd, WM_HOTKEY, hotkey->id, MAKEWORD(hotkey->fsModifiers, hotkey->vk));
+	    return;
+	}
+    }
+
     wine_tsx11_lock();
     if (xic)
         ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, &status);
@@ -1576,15 +1670,36 @@
             {
 		int k;
 
-		for (k = 0; k < keysyms_per_keycode; k += 1)
-                    if (XKeycodeToKeysym(display, *kcp, k) == XK_Num_Lock)
-		    {
-                        NumLockMask = 1 << i;
+		for (k = 0; k < keysyms_per_keycode; k += 1) {
+		    KeySym sym = XKeycodeToKeysym(display, *kcp, k);
+		    switch (sym) {
+		        case XK_Num_Lock: 
+                        NumLockMask |= 1 << i;
                         TRACE_(key)("NumLockMask is %x\n", NumLockMask);
+                        break;
+                        
+		        case XK_Scroll_Lock: 
+                        ScrollLockMask |= 1 << i;
+                        TRACE_(key)("ScrollLockMask is %x\n", ScrollLockMask);
+                        break;
+                        
+		        case XK_Meta_L: case XK_Meta_R:
+                        MetaMask |= 1 << i;
+                        TRACE_(key)("MetaMask is %x\n", MetaMask);
+                        break;
+                        
+		        case XK_Super_L: case XK_Super_R:
+                        SuperMask |= 1 << i;
+                        TRACE_(key)("SuperMask is %x\n", SuperMask);
+                        break;
+
+		        default: break;
 		    }
+		}
             }
     }
     XFreeModifiermap(mmp);
+    AnyLockMask = LockMask|NumLockMask|ScrollLockMask;
 
     /* Detect the keyboard layout */
     X11DRV_KEYBOARD_DetectLayout();
@@ -2521,3 +2636,98 @@
     XBell(thread_display(), 0);
     wine_tsx11_unlock();
 }
+
+/***********************************************************************
+ *		RegisterHotKey (X11DRV.@)
+ */
+BOOL X11DRV_RegisterHotKey(HWND hwnd,INT id,UINT modifiers,UINT vk)
+{
+    Display *display;
+    Window window;
+    BOOL ret;
+    int x_modifiers = 0, grab_mask, ungrab_mask;
+    int x_keycode = 0;
+    int err;
+    int keyc;
+
+    TRACE("(%p,0x%x,0x%08x,0x%08x)\n",hwnd,id,modifiers,vk);
+    
+    display = thread_display();
+    window = X11DRV_get_whole_window(hwnd);
+
+    if (modifiers & MOD_ALT)
+	x_modifiers |= MetaMask;
+    if (modifiers & MOD_CONTROL)
+	x_modifiers |= ControlMask;
+    if (modifiers & MOD_SHIFT)
+	x_modifiers |= ShiftMask;
+    if (modifiers & MOD_WIN)
+	x_modifiers |= SuperMask;
+
+    for (keyc=min_keycode; keyc<=max_keycode; keyc++) {
+	if ((keyc2vkey[keyc] & 0xFF) == vk) {
+	    TRACE("keycode found\n");
+	    x_keycode = keyc;
+	    break;
+	}
+    }
+
+    if (keyc > max_keycode) {
+	TRACE("keycode not found\n");
+	return FALSE;
+    }
+
+    /* We need to register every combination of the locks to ignore them */
+    wine_tsx11_lock();
+    grab_mask = AnyLockMask;
+    do {
+        err = XGrabKey(display, x_keycode, x_modifiers|grab_mask, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
+        if (err == 0) break;
+        grab_mask = (grab_mask-1) & AnyLockMask;
+    } while (grab_mask != AnyLockMask);
+    
+    if (err == 0) {
+	FIXME("failed to grab keycode=0x%04x modifiers=0x%04x", x_keycode, x_modifiers);
+	/* FIXME: set proper error value */
+	SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
+	/* unregister anything we succeeded in grabbing */
+	ungrab_mask = AnyLockMask;
+	while (ungrab_mask != grab_mask) {
+	    XUngrabKey(display, x_keycode, x_modifiers|ungrab_mask, DefaultRootWindow(display));
+	    ungrab_mask = (ungrab_mask-1) & AnyLockMask;
+	}
+	ret = FALSE;	
+    } else {
+	add_grab(hwnd, id, modifiers, vk, x_modifiers, x_keycode);
+	ret = TRUE;
+    }
+    wine_tsx11_unlock();
+
+    return ret;
+}
+
+/***********************************************************************
+ *		UnregisterHotKey (X11DRV.@)
+ */
+BOOL X11DRV_UnregisterHotKey(HWND hwnd,INT id)
+{
+    Display *display = thread_display();
+    Window window = X11DRV_get_whole_window(hwnd);
+    GRABKEY_INFO * grab = find_grab(id);
+    int ungrab_mask;
+    
+    TRACE("(%lx,%d)\n",(DWORD)hwnd,id);
+
+    if (grab) {
+        wine_tsx11_lock();
+        ungrab_mask = AnyLockMask;
+        do {
+	    XUngrabKey(display, grab->x_keycode, grab->x_modifiers|ungrab_mask, window);
+	    ungrab_mask = (ungrab_mask-1) & AnyLockMask;
+	} while (ungrab_mask != AnyLockMask);
+	wine_tsx11_unlock();
+	remove_grab(id);
+	return TRUE;
+    }
+    return FALSE;
+}
Index: dlls/x11drv/winex11.drv.spec
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/winex11.drv.spec,v
retrieving revision 1.5
diff -u -r1.5 winex11.drv.spec
--- dlls/x11drv/winex11.drv.spec	27 Mar 2006 20:51:24 -0000	1.5
+++ dlls/x11drv/winex11.drv.spec	7 May 2006 20:15:38 -0000
@@ -76,6 +76,8 @@
 @ cdecl ToUnicodeEx(long long ptr ptr long long long) X11DRV_ToUnicodeEx
 @ cdecl UnloadKeyboardLayout(long) X11DRV_UnloadKeyboardLayout
 @ cdecl VkKeyScanEx(long long) X11DRV_VkKeyScanEx
+@ cdecl RegisterHotKey(long long long long) X11DRV_RegisterHotKey
+@ cdecl UnregisterHotKey(long long) X11DRV_UnregisterHotKey
 @ cdecl SetCursor(ptr) X11DRV_SetCursor
 @ cdecl GetCursorPos(ptr) X11DRV_GetCursorPos
 @ cdecl SetCursorPos(long long) X11DRV_SetCursorPos
Index: include/winbase.h
===================================================================
RCS file: /home/wine/wine/include/winbase.h,v
retrieving revision 1.253
diff -u -r1.253 winbase.h
--- include/winbase.h	16 Mar 2006 20:41:46 -0000	1.253
+++ include/winbase.h	7 May 2006 20:15:40 -0000
@@ -1844,6 +1844,7 @@
 HANDLE      WINAPI RegisterEventSourceA(LPCSTR,LPCSTR);
 HANDLE      WINAPI RegisterEventSourceW(LPCWSTR,LPCWSTR);
 #define     RegisterEventSource WINELIB_NAME_AW(RegisterEventSource)
+BOOL        WINAPI RegisterHotKey(HWND,INT,UINT,UINT);
 BOOL        WINAPI RegisterWaitForSingleObject(PHANDLE,HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
 HANDLE      WINAPI RegisterWaitForSingleObjectEx(HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
 VOID        WINAPI ReleaseActCtx(HANDLE);
@@ -1971,6 +1972,7 @@
 BOOL        WINAPI UnlockFileEx(HANDLE,DWORD,DWORD,DWORD,LPOVERLAPPED);
 #define     UnlockSegment(handle) GlobalUnfix((HANDLE)(handle))
 BOOL        WINAPI UnmapViewOfFile(LPVOID);
+BOOL        WINAPI UnregisterHotKey(HWND,INT);
 BOOL        WINAPI UnregisterWait(HANDLE);
 BOOL        WINAPI UnregisterWaitEx(HANDLE,HANDLE);
 BOOL        WINAPI UpdateResourceA(HANDLE,LPCSTR,LPCSTR,WORD,LPVOID,DWORD);


More information about the wine-devel mailing list