Tecla rápida global con X11/Xlib
hotkeys (3)
Mi objetivo es tener un programa que se duerma en segundo plano pero que pueda ser activado por el usuario a través de algunas "teclas de acceso rápido". A partir de la exploración del manual Xlib y el manual Xlib O''reilly, deduzco que la forma correcta de hacerlo es con XGrabKey. Sin embargo, mi comprensión del proceso es incorrecta ya que una simple prueba de concepto no funciona.
Tengo entendido que si llamo a XGrabKey con la ventana raíz como grab_window y owner_events false, cada vez que presione mi tecla de acceso rápido, el evento se enviará solo a la ventana raíz. Si luego selecciono los eventos de KeyPress en la ventana raíz, y luego escucho los eventos de X, debo obtener un evento de pulsación de tecla cuando se presiona la tecla de acceso rápido. He pegado un ejemplo mínimo a continuación.
Lo que espero es que cuando se ejecute el programa, independientemente de qué ventana tenga el foco, si se presiona Ctrl + Shift + K, mi programa debería mostrar "Tecla de acceso rápido presionada!" en la consola, y luego terminar.
Además, entiendo que si XGrabKey falla, el controlador de errores predeterminado mostrará un mensaje y, como no lo hace, asumo que la llamada se realizó correctamente.
Obviamente, mi entendimiento es defectuoso de alguna manera. ¿Alguien puede señalarme en la dirección correcta?
#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;
}
Con tu máscara ControlMask | ShiftMask
ControlMask | ShiftMask
no obtendrá la clave si se mantiene presionada otra tecla modificadora. Esto suena bien en primer lugar, pero hay un escollo: NumLock , CapsLock y todos ellos también son tratados como modificadores.
Tienes dos opciones:
-
XGrabKey()
aXGrabKey()
varias veces, una por cada combinación explícita que te interesa. - Llama a
XGrabKey()
conAnyModifier
y usaevent.xkey.state
para verificar si los modificadores son los que esperaba.
El archivo de encabezado <Xh>
define ShiftMask
, LockMask
, ControlMask
, Mod1Mask
, Mod2Mask
, Mod3Mask
, Mod4Mask
, Mod5Mask
y AnyModifier
.
Las claves son:
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 | ???
Advertencia: Me enteré de las claves ModNMask
al intentarlo y no sé si esto es válido en todas las máquinas / configuraciones / versiones / sistemas operativos.
En su caso, probablemente quiera asegurarse de que ShiftMask | CtrlMask
ShiftMask | CtrlMask
está establecido, Mod1Mask | Mod4Mask
Mod1Mask | Mod4Mask
son claros, y los demás deben ser ignorados.
Haría esto para configurar la captura de clave:
XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);
Y esto para comprobar si los modificadores correctos están configurados:
switch (ev.type) {
case KeyPress:
if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
// ...
}
Si estás usando / apuntando a gtk en X11, hay una biblioteca de C con una interfaz mucho más simple:
https://github.com/engla/keybinder
Incluye fijaciones Python, Lua y Vala. (También mencionado here ).
Su programa funciona aquí. Mi conjetura es que tienes otro modificador activo, como NumLock. GrabKey solo funciona en la máscara modificadora exacta .
Por ejemplo, aquí hay un código (GPL) del administrador de ventanas de metacity
/* 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);
}