Linux X11-Global Keyboard Hook

est-il possible (ou comment) de créer un mécanisme (sous Linux X11, C++) qui fonctionne comme un crochet global sous windows (SetWindowsHookEx())?

je voudrais être en mesure d'attraper l'événement clé, mais avec la possibilité de propagation ultérieure . J'essaie d'utiliser une solution XGrabKey (comme dans xbindkeys ) mais quand je règle capturer l'événement clé, cet événement est "consommé".

les prescriptions applicables à ce mécanisme sont les suivantes:

  1. / Globale à l'échelle du système de captation d'événements, indépendamment de la fenêtre qui a le focus
  2. La possibilité de "catch-hold" et "catch-passer à travers"
  3. Il doit être assez rapide

exemple de code ressemble à ceci:

bool myFlagIsSet = false;
XEvent event;
while (true) {
    while (XPending(display) > 0) {
        usleep(SLEEP_TIME);
    }

    XNextEvent(display, &event);
    switch (e.type) {
        case KeyPress:
            if (myFlagIsSet) {
                //do not propagate
            }
            // propagate
            break;
        case KeyRelease:
            if (myFlagIsSet) {
                //do not propagate
            }
            // propagate
            break;
    }
}

sur Windows j'ai simplement écrit:

if(event.isConsumed()) {
    return LRESULT(1);
}
//...
return CallNextHookEx(hookHandle, nCode, wParam, lParam);

j'ai aussi essayé D'utiliser XUngrabKey et XSendEvent:

switch (event.type) {
    case KeyPress:
        if (myFlagIsSet) {
            //do not propagate
        }
        // propagate
        XUngrabKey(...);
        XSendEvent(..., &event);
        XGrabKey(...);
        break;
    case KeyRelease:
        ...
    }

malheureusement XSendEvent pour des raisons inconnues à moi - n'envoyez pas cet événement même si la ligne XGrabKey est commentée.

Est-il possible de compléter avec succès cette approche?

s'il vous Plaît suggérer une autre approche si je suis condamné à l'échec", 151940920"

MODIFIER

je voudrais mettre en œuvre ce sur Ubuntu Gnome en utilisant Compiz Window Manager""

26
demandé sur Community 2011-07-03 05:28:25

5 réponses

XSendEvent() l'envoie probablement; mais comme il est largement considéré comme un trou de sécurité, la plupart des programmes ignorent les événements UI avec le drapeau send_event .

le protocole X11 standard ne le permet pas. L'extension xinput 2.0 pourrait, mais j'en doute; alors que Windows suppose une file d'attente d'événements unique que chaque programme écoute, de sorte qu'un programme peut intercepter un événement et l'empêcher d'être envoyé en file d'attente à d'autres auditeurs, chaque client X11 a sa propre la file d'attente indépendante et tous les clients qui s'intéressent à un événement reçoivent une copie indépendante de celui-ci dans leur file d'attente. Cela signifie que dans des circonstances normales, il est impossible pour un programme errant de bloquer d'autres programmes; mais cela signifie également que, pour les fois où un client doit bloquer d'autres clients, il doit faire une capture de serveur pour empêcher le serveur de traiter des événements pour tout autre client.

3
répondu geekosaur 2011-07-03 03:15:37

utilisez XTestFakeKeyEvent () de la xtest extension library pour propager de faux événements de presse / publication de clés.

2
répondu fizzer 2011-07-03 08:28:55

au lieu de le faire au niveau X11, je recommande de le faire au niveau du périphérique d'entrée. /dev/input/event<n> vous donne les événements d'entrée. Vous pouvez y lire les touches et décider si elles doivent se propager OU être consommées. Malheureusement, il n'y a pas de véritable documentation pour cela, mais le fichier d'en-tête linux/include/input.h est assez explicite. De plus, le responsable d'evdev répondra volontiers aux e-mails.

2
répondu datenwolf 2011-07-12 23:54:22

aucune idée si cela aide, mais je viens de trouver ce code:



    void XFakeKeypress(Display *display, int keysym)
    { 
       XKeyEvent event;                     
       Window current_focus_window;         
       int current_focus_revert;

       XGetInputFocus(/* display = */ display, /* focus_return = */ 
          &current_focus_window, /* revert_to_return = */ &current_focus_revert);

       event.type = /* (const) */ KeyPress;
       event.display = display;
       event.window = current_focus_window;
       event.root = DefaultRootWindow(/* display = */ display);
       event.subwindow = /* (const) */ None;
       event.time = 1000 * time(/* tloc = */ NULL);
       event.x = 0;
       event.y = 0;
       event.x_root = 0;
       event.y_root = 0;
       event.state = /* (const) */ ShiftMask;   
       event.keycode = XKeysymToKeycode(/* display = */ display, 
          /* keysym = */ keysym);
       event.same_screen = /* (const) */ True;  

       XSendEvent(/* display = */ display, /* w = (const) */ InputFocus,
          /* propagate = (const) */ True, /* event_mask = (const) */ 
          KeyPressMask, /* event_send = */ (XEvent *)(&event));

       event.type = /* (const) */ KeyRelease;   
       event.time = 1000 * time(/* tloc = */ NULL);

       XSendEvent(/* display = */ display, /* w = (const) */ InputFocus,
          /* propagate = (const) */ True, /* event_mask = (const) */ 
          KeyReleaseMask, /* event_send = */ (XEvent *)(&event));
    }

1
répondu Coder of Salvation 2012-03-17 15:37:45

essayez de compiler le code facile à partir de cette page:

http://webhamster.ru/site/page/index/articles/comp/367

c'est un exemple d'événement get global keyboard. Cette petite application fonctionne comme xinput .

Remarque 1: Ecrire l'ID du périphérique à mian.rpc (obtenir l'ID en cours d'exécution xinput sans paramètres):

sprintf(deviceId, "9");

Remarque 2: compiler la commande:

gcc ./main.cpp -lstdc++ -lX11 -lXext -lXi

Remakr 3: avant de compiler, installer le paquet libxi-dev:

apt-get install libxi-dev

Fichier principal.h

#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>

#ifdef HAVE_XI2
#include <X11/extensions/XInput2.h>
#endif

#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>

extern int xi_opcode; /* xinput extension op code */

XDeviceInfo* find_device_info( Display *display, char *name, Bool only_extended);

#if HAVE_XI2
XIDeviceInfo* xi2_find_device_info(Display *display, char *name);
int xinput_version(Display* display);
#endif

Fichier principal.cpp

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "main.h"
#include <ctype.h>
#include <string.h>

using namespace std;

int xi_opcode;

#define INVALID_EVENT_TYPE -1

static int motion_type = INVALID_EVENT_TYPE;
static int button_press_type = INVALID_EVENT_TYPE;
static int button_release_type = INVALID_EVENT_TYPE;
static int key_press_type = INVALID_EVENT_TYPE;
static int key_release_type = INVALID_EVENT_TYPE;
static int proximity_in_type = INVALID_EVENT_TYPE;
static int proximity_out_type = INVALID_EVENT_TYPE;

static int register_events(Display *dpy,
                           XDeviceInfo *info,
                           char *dev_name,
                           Bool handle_proximity)
{
    int            number = 0;    /* number of events registered */
    XEventClass        event_list[7];
    int            i;
    XDevice        *device;
    Window        root_win;
    unsigned long    screen;
    XInputClassInfo    *ip;

    screen = DefaultScreen(dpy);
    root_win = RootWindow(dpy, screen);

    device = XOpenDevice(dpy, info->id);

    if (!device) {
    printf("unable to open device '%s'\n", dev_name);
    return 0;
    }

    if (device->num_classes > 0) {
    for (ip = device->classes, i=0; i<info->num_classes; ip++, i++) {
        switch (ip->input_class) {
        case KeyClass:
        DeviceKeyPress(device, key_press_type, event_list[number]); number++;
        DeviceKeyRelease(device, key_release_type, event_list[number]); number++;
        break;

        case ButtonClass:
        DeviceButtonPress(device, button_press_type, event_list[number]); number++;
        DeviceButtonRelease(device, button_release_type, event_list[number]); number++;
        break;

        case ValuatorClass:
        DeviceMotionNotify(device, motion_type, event_list[number]); number++;
        if (handle_proximity) {
            ProximityIn(device, proximity_in_type, event_list[number]); number++;
            ProximityOut(device, proximity_out_type, event_list[number]); number++;
        }
        break;

        default:
        printf("unknown class\n");
        break;
        }
    }

    if (XSelectExtensionEvent(dpy, root_win, event_list, number)) {
        printf("error selecting extended events\n");
        return 0;
    }
    }
    return number;
}


static void print_events(Display *dpy)
{
    XEvent        Event;

    setvbuf(stdout, NULL, _IOLBF, 0);

    while(1) {
    XNextEvent(dpy, &Event);

    if (Event.type == motion_type) {
        int    loop;
        XDeviceMotionEvent *motion = (XDeviceMotionEvent *) &Event;

        printf("motion ");

        for(loop=0; loop<motion->axes_count; loop++) {
        printf("a[%d]=%d ", motion->first_axis + loop, motion->axis_data[loop]);
        }
        printf("\n");
    } else if ((Event.type == button_press_type) ||
           (Event.type == button_release_type)) {
        int    loop;
        XDeviceButtonEvent *button = (XDeviceButtonEvent *) &Event;

        printf("button %s %d ", (Event.type == button_release_type) ? "release" : "press  ",
           button->button);

        for(loop=0; loop<button->axes_count; loop++) {
        printf("a[%d]=%d ", button->first_axis + loop, button->axis_data[loop]);
        }
        printf("\n");
    } else if ((Event.type == key_press_type) ||
           (Event.type == key_release_type)) {
        int    loop;
        XDeviceKeyEvent *key = (XDeviceKeyEvent *) &Event;

        printf("key %s %d ", (Event.type == key_release_type) ? "release" : "press  ",
           key->keycode);

        for(loop=0; loop<key->axes_count; loop++) {
        printf("a[%d]=%d ", key->first_axis + loop, key->axis_data[loop]);
        }
        printf("\n");
    } else if ((Event.type == proximity_out_type) ||
           (Event.type == proximity_in_type)) {
        int    loop;
        XProximityNotifyEvent *prox = (XProximityNotifyEvent *) &Event;

        printf("proximity %s ", (Event.type == proximity_in_type) ? "in " : "out");

        for(loop=0; loop<prox->axes_count; loop++) {
        printf("a[%d]=%d ", prox->first_axis + loop, prox->axis_data[loop]);
        }
        printf("\n");
    }
    else {
        printf("what's that %d\n", Event.type);
    }
    }
}


// Определение версии библиотеки расширений, установленной для X11
int xinput_version(Display    *display)
{
    XExtensionVersion    *version;
    static int vers = -1;

    if (vers != -1)
        return vers;

    version = XGetExtensionVersion(display, INAME);

    if (version && (version != (XExtensionVersion*) NoSuchExtension)) {
    vers = version->major_version;
    XFree(version);
    }

#if HAVE_XI2
    /* Announce our supported version so the server treats us correctly. */
    if (vers >= XI_2_Major)
    {
        const char *forced_version;
        int maj = 2,
            min = 0;

#if HAVE_XI22
        min = 2;
#elif HAVE_XI21
        min = 1;
#endif

        forced_version = getenv("XINPUT_XI2_VERSION");
        if (forced_version) {
            if (sscanf(forced_version, "%d.%d", &maj, &min) != 2) {
                fprintf(stderr, "Invalid format of XINPUT_XI2_VERSION "
                                "environment variable. Need major.minor\n");
                exit(1);
            }
            printf("Overriding XI2 version to: %d.%d\n", maj, min);
        }

        XIQueryVersion(display, &maj, &min);
    }
#endif

    return vers;
}


// Поиск информации об устройстве
XDeviceInfo* find_device_info(Display *display,
                              char *name,
                              Bool only_extended)
{
    XDeviceInfo *devices;
    XDeviceInfo *found = NULL;
    int        loop;
    int        num_devices;
    int        len = strlen(name);
    Bool    is_id = True;
    XID        id = (XID)-1;

    for(loop=0; loop<len; loop++) {
    if (!isdigit(name[loop])) {
        is_id = False;
        break;
    }
    }

    if (is_id) {
    id = atoi(name);
    }

    devices = XListInputDevices(display, &num_devices);

    for(loop=0; loop<num_devices; loop++) {
    if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) &&
        ((!is_id && strcmp(devices[loop].name, name) == 0) ||
         (is_id && devices[loop].id == id))) {
        if (found) {
            fprintf(stderr,
                    "Warning: There are multiple devices named '%s'.\n"
                    "To ensure the correct one is selected, please use "
                    "the device ID instead.\n\n", name);
        return NULL;
        } else {
        found = &devices[loop];
        }
    }
    }
    return found;
}


int test(Display *display, char *deviceId)
{
    XDeviceInfo *info;

    Bool handle_proximity = True;

    info = find_device_info(display, deviceId, True);

    if(!info)
    {
      printf("unable to find device '%s'\n", deviceId);
      exit(1);
    }
    else
    {
      if(register_events(display, info, deviceId, handle_proximity))
         print_events(display);
      else
      {
        fprintf(stderr, "no event registered...\n");
        exit(1);
      }
    }

    return 0;
}


int main()
{
  Display *display;
  int event, error;

  // Инициируется указатель на текущий дисплей
  display = XOpenDisplay(NULL);
  if (display == NULL)
  {
    printf("Unable to connect to X server\n");
    exit(1);
  }

  // Проверяется наличие расширений
  if(!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error))
  {
    printf("X Input extension not available.\n");
    exit(1);
  }

  // Проверяется версия расширения, она не должна быть нулевой
  if(!xinput_version(display))
  {
    printf("%s extension not available\n", INAME);
    exit(1);
  }

  char deviceId[10];
  sprintf(deviceId, "9");

  test(display, deviceId);

  XSync(display, False);
  XCloseDisplay(display);

  return 0;
}
1
répondu Xintrea 2016-06-10 05:42:30