LINUX.ORG.RU

Отловить нажатие комбинации клавиш в XLib

 ,


0

1

Добрый день Мне нужно отловить нажатие CTRL+Y в иксах во всех окнах. Написал небольшую программу. Запускается но не работает. Выполнение останавливается на строке с вызовом XNextEvent. Помогите понять, в чем проблема. Спасибо.


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;

int main()
{
    XEvent event;
    Display *dis;
    Window root;
    Bool owner_events = False;
    unsigned int modifiers = ControlMask;


    dis = XOpenDisplay(NULL);
    root = XDefaultRootWindow(dis);
    unsigned int keycode = XKeysymToKeycode(dis, XK_Y);
    XSelectInput(dis,root, KeyPressMask);
    XGrabKey(dis, keycode, modifiers, root, owner_events, GrabModeAsync, GrabModeAsync);

    while (1) {
        Bool QuiteCycle = False;
        XNextEvent(dis, &event);
        if (event.type == KeyPress) {
            cout << "Hot key pressed!" << endl;
            XUngrabKey(dis, keycode, modifiers, root);

        }
        if (QuiteCycle) {
            break;
        }
    }
    XCloseDisplay(dis);
    return 0;
}

на самом деле, забыл все за пять лет, но попробуй «while(1)» заметить на «while(XPending(dis))»

metawishmaster ★★★★ ()

XUngrabKey(dis, keycode, modifiers, root);

Вот это ещё можно попробовать убрать / вынести из цикла.

quasimoto ★★★★ ()

drupaler

Может все-таки лучше PHP?

grouzen ★★ ()
Ответ на: комментарий от anonymous

xcb же лучше

Только в сферических программах в вакууме.

baverman ★★★ ()

Выполнение останавливается на строке с вызовом XNextEvent

Останавливается или блокируется? Если второе, то все правильно.

baverman ★★★ ()
Ответ на: комментарий от baverman

Как оказалось, проблема была в том, что был нажат Num Lock. Как только я его отключил - все заработало. Только вот появился небольшой косяк в связи с тем, что если моя прога перехватывает комбинацию клавиш, то другие проги перестают на нее реагировать. Например, я начинаю перехватывать CTRL+P, то печать с помощью этих клавиш перестает работать. Подскажите, как можно сделать так, чтобы моя прога не блокировала комбинацию. То есть, я хочу получить событие, что клавиша нажата, выполнить действия и передать нажатую комбинацию тому окну, из которого была вызвана комбинация.

drupaler ()
Ответ на: комментарий от drupaler

То есть, я хочу получить событие, что клавиша нажата, выполнить действия и передать нажатую комбинацию тому окну, из которого была вызвана комбинация.

Посылать самим окну в фокусе?

Window focus(Display *d) {
    Window f, w;
    int n; unsigned int m;
    do XQueryPointer(d, XDefaultRootWindow(d), &f, &w, &n, &n, &n, &n, &m);
    while (w <= 0);
    return w;
}

            cout << "Hot key pressed!" << endl;

            const Window w = focus(dis);
            static XKeyEvent e;
            e.display = dis;
            e.window = w;
            e.time = CurrentTime;
            e.type = KeyPress;
            e.state = ControlMask;
            e.keycode = keycode;
            XSendEvent(dis, w, True, KeyPressMask, (XEvent*)&e);
            e.type = KeyRelease;
            XSendEvent(dis, w, True, KeyPressMask, (XEvent*)&e);
quasimoto ★★★★ ()
Ответ на: комментарий от quasimoto

Большое спасибо за наводку. Я и сам думал об использовании XSendEvent, просто хотел найти более простой способ. А можно как-то без XSendEvent? Неужели в иксах нет возможности ловить нажатии клавиш без блокирования клавиатуры?

drupaler ()
Ответ на: комментарий от quasimoto

Либо не делать XGrabKey/XUngrabKey, а просто слушать по XSelectInput на всех интересующих окнах:

    XSelectInput(d, w, KeyPressMask);
    XEvent e;
    while (1) {
        XNextEvent(d, &e);
        if (e.type == KeyPress &&
            e.xkey.state == m_ctrl &&
            e.xkey.keycode == k_y) {
            // ...          
        }
    }

так событие само будет доставляться.

quasimoto ★★★★ ()
Ответ на: комментарий от quasimoto

Либо не делать XGrabKey/XUngrabKey, а просто слушать по XSelectInput на всех интересующих окнах

Убрал XGrabKey и XungrabKey - перестало работать. Может это потому, что я повесил XSelectInput на дефолтное окно, а нужно в цикле пройтись по всем открытым окнам?

Вот мой код сейчас:

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

using namespace std;


int main()
{
    XEvent event;
    Display *dis;
    Window root;
    Bool owner_events = True;
    unsigned int modifiers = ControlMask;


    dis = XOpenDisplay(NULL);
    root = XDefaultRootWindow(dis);
    unsigned int keycode = XKeysymToKeycode(dis, XK_P);
    XSelectInput(dis, root, KeyPressMask);
    //XGrabKey(dis, keycode, modifiers, root, owner_events, GrabModeAsync, GrabModeAsync);

    while (1) {
        Bool QuiteCycle = False;
        XNextEvent(dis, &event);
        if (event.type == KeyPress) {
            cout << "Hot key pressed!" << endl;
            //XUngrabKey(dis, keycode, modifiers, root);
            QuiteCycle = True;
        }
        if (QuiteCycle) {
            break;
        }
    }
    XCloseDisplay(dis);
    return 0;
}

drupaler ()
Ответ на: комментарий от drupaler

В отличии от XGrabKey на root окне XSelectInput на root будет слушать только его события, да.

Обойти все окна можно

struct Root {

    Display *display;
    Window root;

    Root() : display(XOpenDisplay(0)), root(XDefaultRootWindow(display)) {}

    ~Root() { XCloseDisplay(display); }

    template <typename F>
    void traverse(Window w, F action) {

        action(w);

        Window r, p, *cs; unsigned n;
        if (0 != XQueryTree(display, w, &r, &p, &cs, &n)) {
            for (unsigned i = 0; i < n; ++i)
                traverse(cs[i], action);
            XFree(cs);
        }
    }

    template <typename F>
    void traverse(F action) { traverse(root, action); }

};

И слушать на всех:

int main() {

    Root r;

    const auto m_ctrl = ControlMask;
    const auto k_y = XKeysymToKeycode(r.display, XK_Y);

    r.traverse([&](Window w) { XSelectInput(r.display, w, KeyPressMask); });

    XEvent e;
    while (1) {
        XNextEvent(r.display, &e);
        if (e.type == KeyPress && e.xkey.state == m_ctrl && e.xkey.keycode == k_y)
            puts("Hot key pressed!");
    }

}

Но это как-то не комильфо и может быть бажно. Если слушать нужно на чужих окнах, но не на всех, а с известными PID-ами, то как-то

struct PID {

    Display *display;
    Atom net_wm_pid;

    PID(Display *display) :
        display(display),
        net_wm_pid(XInternAtom(display, "_NET_WM_PID", True)) {}

    template <typename F>
    void match(Window w, F action) {
        Atom t; int f; unsigned long ni, nb;
        unsigned char *prop = 0;
        if (Success == XGetWindowProperty
            (display, w, net_wm_pid, 0, 1, False, XA_CARDINAL,
             &t, &f, &ni, &nb, &prop)) {
            if (prop != 0) {
                action(*(pid_t*)prop);
                XFree(prop);
            }
        }
    }

};

и выбирать нужные

    r.traverse(
        [&](Window w) {
            PID(r.display).match(
                w, [&](pid_t pid) { 
                    // `w` from `pid`, so...
                });});

Но в смысле глобального хоткея XGrabKey + XSendEvent на focus тут как-то проще, можно ли без XSendEvent — не знаю, по идее owner_events = True должно работать, но нет.

quasimoto ★★★★ ()
Ответ на: комментарий от drupaler

Ещё man XKeyEvent — там же subwindow прилетает, так что focus не нужен, вот такой вариант у меня работает:

#include <cassert>
#include <cstdio>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

int main() {
    const auto d = XOpenDisplay(0);
    const auto r = XDefaultRootWindow(d);
    const auto m_ctrl = ControlMask;
    const auto k_y = XKeysymToKeycode(d, XK_Y);
    XGrabKey(d, k_y, m_ctrl, r, False, GrabModeAsync, GrabModeAsync);
    XEvent e;
    while (1) {
        XNextEvent(d, &e);
        if (e.type == KeyPress) {
            assert(e.xkey.state == m_ctrl && e.xkey.keycode == k_y);
            printf("hot key pressed on %lu, resending...\n", e.xkey.subwindow);
            const auto w = e.xkey.subwindow;
            static XKeyEvent e;
            e.display = d;
            e.window = w;
            e.time = CurrentTime;
            e.type = KeyPress;
            e.state = m_ctrl;
            e.keycode = k_y;
            XSendEvent(d, w, True, KeyPressMask, (XEvent*)&e);
            e.type = KeyRelease;
            XSendEvent(d, w, True, KeyPressMask, (XEvent*)&e);
        }
    }
}
quasimoto ★★★★ ()
Последнее исправление: quasimoto (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.