【问题标题】:Why does XGrabKey return BadRequest?为什么 XGrabKey 返回 BadRequest?
【发布时间】:2015-09-08 18:30:11
【问题描述】:

所以我正在开发一个 Gtk/X11/Linux app,它可以对 .gif 进行屏幕捕获,而停止捕获的方法之一是按键(Esc、空格或结束)。您也可以使用超时。但是,要实现按键以结束捕获,我必须能够抓住键,以便即使我的窗口没有焦点(在捕获期间它实际上是不可见的)我也可以获得事件。我相信XGrabKey 是该任务的正确 X11 函数:

Window w = Gtk::gdk_x11_drawable_get_xid(Gtk::gtk_widget_get_window(Handle()));
KeyCode kc = XKeysymToKeycode(Gtk::gdk_display, HotKeyCode);
int r = XGrabKey(   Gtk::gdk_display,
                    kc,
                    0               /* modifiers */,
                    w               /* grab_window */,
                    TRUE            /* owner_events */,
                    GrabModeAsync   /* pointer_mode */,
                    GrabModeAsync   /* keyboard_mode */);
printf("XGrabKey(%p, 0x%x/%x)=%i\n", w, HotKeyCode, kc, r);

'HotKeyCode' 是说 XK_Escape 或其他东西,例如:

XGrabKey(0x3e00003, 0xff1b/9)=1

XGrabKey 正在返回“1”或 BadRequest。我在这里做错了什么?

仅供参考,实际的 Xorg Xserver 代码似乎是 here

编辑:代码的最新化身是:

int x_err_callback(Display *d, XErrorEvent *e)
{
    char msg[256];

    XGetErrorText(d, e->error_code, msg, sizeof(msg));

    printf("X11Error %d (%s): request %d.%d\n",
        e->error_code, msg, e->request_code,
        e->minor_code);

    return 0;
}

Gtk::GdkFilterReturn key_filter(Gtk::GdkXEvent *gdk_xevent,
                                Gtk::GdkEvent *event,
                                Gtk::gpointer data)
{
    XKeyEvent *xevent = gdk_xevent;
    if (xevent->type == KeyPress)
    {
        int key = ((XKeyEvent *)gdk_xevent)->keycode;
        int keysym = XKeycodeToKeysym(Gtk::gdk_display, key, 0);

        printf("caught keysym %i\n", keysym);

        switch (keysym)
        {
            case 1: // your_keysym
                // your key handler code
                break;
        }
    }

    return Gtk::GDK_FILTER_CONTINUE;
}


Gtk::GdkWindow *Root = Gtk::gdk_get_default_root_window();
KeyCode kc = XKeysymToKeycode(Gtk::gdk_display, HotKeyCode);

XSetErrorHandler(x_err_callback);

int r = XGrabKey(   Gtk::gdk_display,
                    kc,
                    AnyModifier /* modifiers */,
                    GDK_WINDOW_XWINDOW(Root) /* grab_window */,
                    TRUE            /* owner_events */,
                    GrabModeAsync   /* pointer_mode */,
                    GrabModeSync    /* keyboard_mode */);

Gtk::gdk_window_set_events(Root,
    (Gtk::GdkEventMask)
    (Gtk::GDK_KEY_PRESS_MASK |
    Gtk::GDK_KEY_RELEASE_MASK));
Gtk::gdk_window_add_filter(NULL, key_filter, this);

AnyModifier 实际上会导致错误。 “0”没有。我知道 NumLock 问题...

【问题讨论】:

    标签: gtk x11


    【解决方案1】:

    1 的返回值表示发生了 BadRequest 错误。 Xlib handles errors via an error handler,并且函数将总是返回1,如果它返回的话。

    您的代码不起作用,因为您必须在根窗口 (GetDefaultRootWindow(Gtk::gdk_display)) 上执行 XGrabKey。这是一个纯 Xlib 演示:

    #include <X11/Xlib.h>
    #include <X11/keysym.h>
    #include <stdio.h>
    
    int main() {
        Display *d = XOpenDisplay(0);
        Window root = DefaultRootWindow(d);
        int keycode = XKeysymToKeycode(d, XK_BackSpace);
    
        int rv = XGrabKey(d, keycode, AnyModifier, root, 1, GrabModeAsync, GrabModeAsync);
        printf("XGrabKey returned %d\n", rv);
    
        XEvent evt;
        while(1) {
            XNextEvent(d, &evt);
            printf("Got event %d\n", evt.type);
        }
    }
    

    要从 GTK 中捕获 X11 事件,请在 NULL 或根窗口上使用 gdk_window_add_filter,并使用 GdkFilterFunc 处理与您的全局热键关联的事件:

    #include <X11/Xlib.h>
    #include <X11/keysym.h>
    #include <gtk/gtk.h>
    #include <gdk/gdkx.h>
    #include <stdio.h>
    
    GdkFilterReturn filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) {
        XKeyEvent *ev = (XKeyEvent *)xevent;
        if(ev->type == 2) {
            printf("Backspace hit.\n");
        }
    
        return GDK_FILTER_CONTINUE;
    }
    
    int main(int argc, char *argv[]) {
        gtk_init(&argc, &argv);
    
        GdkScreen *scr = gdk_screen_get_default();
        GdkWindow *groot = gdk_screen_get_root_window(scr);
        gdk_window_set_events(groot, GDK_KEY_PRESS_MASK);
        gdk_window_add_filter(groot, filter, NULL);
    
        Display *d = gdk_x11_get_default_xdisplay();
        Window root = GDK_WINDOW_XID(groot);
        int keycode = XKeysymToKeycode(d, XK_BackSpace);
        XGrabKey(d, keycode, AnyModifier, root, 1, GrabModeAsync, GrabModeAsync);
    
        gtk_main();
    }
    

    作为旁注,0 的修饰符掩码意味着必须启用 no 修饰符,即使是那些不会修改键含义的修饰符。使用 0 修饰符抓取字母“A”与 NumLock + A 不匹配。这就是我使用 AnyModifer 的原因。

    【讨论】:

    • 我花了几个小时试图让 gdk_window_add_filter 在根窗口上工作,但没有运气。它不会向我的回调发送任何事件。即使我首先使用 KeyPress/KeyRelease 掩码调用 gdk_window_set_events(root_win, ...) 。至于传递给 XGrabKey 的窗口,我正在做 GDK_WINDOW_XID(root)... 其中 root 来自 gdk_get_default_root_window()。似乎以前没有人从 GTK 应用程序中这样做过。看起来你在正确的轨道上,只是没有让我越界。 (我也设置了错误处理程序......它没有显示任何有趣的东西)
    • 在独立的 X11 进程中,您的代码可以工作(减去 AnyModifier 参数)。我不能让它在我的 GTK 应用程序的上下文中工作。我暂时可能做的是运行一个子进程来做键盘钩子并将您的代码用作模板。
    • 为我工作。我将添加在 GTK 主循环中运行的第二个示例(用 C 语言)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 2022-08-17
    相关资源
    最近更新 更多