Global Hotkey avec X11/Xlib
mon but est d'avoir un programme qui dort en arrière-plan mais peut être activé par l'utilisateur via un certain"hotkey". En fouillant autour du manuel de Xlib et du manuel de Xlib o'Reilly, j'ai compris que la bonne façon d'y arriver est avec XGrabKey. Cependant, ma compréhension du processus est incorrecte comme une simple preuve de concept ne fonctionne pas.
ma compréhension est que si J'appelle XGrabKey avec la fenêtre root comme la fenêtre grab_window, et owner_events false, puis chaque fois que mon hotkey est pressé l'événement sera envoyé seulement à la fenêtre racine. Si je sélectionne alors les évènements KeyPress à partir de la fenêtre root, et que j'écoute ensuite les évènements X, je devrais obtenir un évènement key press lorsque la touche hotkey est pressée. J'ai collé un exemple minime ci-dessous.
ce que j'attends c'est que quand le programme est lancé, indépendamment de ce que la fenêtre a mis au point, si Ctrl+Shift+K est pressé, mon programme devrait sortir "touche chaude pressée!"dans la console, puis terminer.
de plus, je crois comprendre que si XGrabKey échoue, le gestionnaire d'erreurs par défaut affichera un message, et puisqu'il ne le fait pas, je suppose que l'appel réussit.
de toute évidence, ma compréhension est imparfaite. Quelqu'un peut me pointer dans la bonne direction?
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main()
{
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = ControlMask | ShiftMask;
int keycode = XKeysymToKeycode(dpy,XK_Y);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
{
bool shouldQuit = false;
XNextEvent(dpy, &ev);
switch(ev.type)
{
case KeyPress:
cout << "Hot key pressed!" << endl;
XUngrabKey(dpy,keycode,modifiers,grab_window);
shouldQuit = true;
default:
break;
}
if(shouldQuit)
break;
}
XCloseDisplay(dpy);
return 0;
}
3 réponses
Votre programme fonctionne ici. Je pense que vous avez un autre modificateur actif, comme NumLock. GrabKey ne fonctionne que sur le masque modificateur exact .
par exemple voici un code (GPL) de metacity window manager
/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
static void
meta_change_keygrab (MetaDisplay *display,
Window xwindow,
gboolean grab,
int keysym,
unsigned int keycode,
int modmask)
{
unsigned int ignored_mask;
/* Grab keycode/modmask, together with
* all combinations of ignored modifiers.
* X provides no better way to do this.
*/
meta_topic (META_DEBUG_KEYBINDINGS,
"%s keybinding %s keycode %d mask 0x%x on 0x%lx\n",
grab ? "Grabbing" : "Ungrabbing",
keysym_name (keysym), keycode,
modmask, xwindow);
/* efficiency, avoid so many XSync() */
meta_error_trap_push (display);
ignored_mask = 0;
while (ignored_mask <= display->ignored_modifier_mask)
{
if (ignored_mask & ~(display->ignored_modifier_mask))
{
/* Not a combination of ignored modifiers
* (it contains some non-ignored modifiers)
*/
++ignored_mask;
continue;
}
if (meta_is_debugging ())
meta_error_trap_push_with_return (display);
if (grab)
XGrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow,
True,
GrabModeAsync, GrabModeSync);
else
XUngrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow);
if (meta_is_debugging ())
{
int result;
result = meta_error_trap_pop_with_return (display, FALSE);
if (grab && result != Success)
{
if (result == BadAccess)
meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask);
else
meta_topic (META_DEBUG_KEYBINDINGS,
"Failed to grab key %s with modifiers %x\n",
keysym_name (keysym), modmask | ignored_mask);
}
}
++ignored_mask;
}
meta_error_trap_pop (display, FALSE);
}
si vous utilisez / ciblez gtk sur X11, il y a une bibliothèque C avec une interface beaucoup plus simple:
https://github.com/engla/keybinder
comprend les fixations Python, Lua et Vala. (Également mentionné ici .)
avec votre masque ControlMask | ShiftMask
vous n'obtiendrez pas la clé si une autre clé modificateur est détenue. Cela semble correct en premier lieu, mais il ya un piège: NumLock , CapsLock et tous les mêmes sont traités comme des modificateurs, aussi.
vous avez deux options:
- vous appelez
XGrabKey()
plusieurs fois, une fois pour chaque combinaison explicite qui vous intéresse. - vous appelez
XGrabKey()
avecAnyModifier
et utilisezevent.xkey.state
pour vérifier si les modificateurs sont comme vous l'aviez prévu.
le fichier d'en-tête <X.h>
définit ShiftMask
, LockMask
, ControlMask
, Mod1Mask
, Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
et AnyModifier
.
les clés sont:
Mask | Value | Key
------------+-------+------------
ShiftMask | 1 | Shift
LockMask | 2 | Caps Lock
ControlMask | 4 | Ctrl
Mod1Mask | 8 | Alt
Mod2Mask | 16 | Num Lock
Mod3Mask | 32 | Scroll Lock
Mod4Mask | 64 | Windows
Mod5Mask | 128 | ???
Avertissement j'ai découvert l' ModNMask
essayer et je ne sais pas si cela est valable sur toutes les machines / configurations / versions / systèmes d'exploitation.
dans votre cas, vous voulez probablement vous assurer que ShiftMask | CtrlMask
est défini, Mod1Mask | Mod4Mask
sont claires, et les autres à ignorer.
je le ferais pour configurer la clé grab:
XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);
et ceci pour vérifier si les bons modificateurs sont définis:
switch (ev.type) {
case KeyPress:
if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
// ...
}