在Xlib中捕获按钮事件,然后将事件传递给客户端

问题描述 投票:0回答:1

我正在开发一个窗口管理器,主要是作为练习,我遇到了一个问题。我希望能够将单击的窗口提升到堆栈顶部。目前,我在 Button1 和 ControlMask 上使用 XGrabButton 来允许移动窗口,当我按住 Ctrl 键并单击窗口时,即可实现所需的效果。但是,如果我在带有 AnyModifier 的 Button1 上使用 XGrabButton,虽然实现了我想要的效果,但我无法再通过鼠标按钮与客户端窗口交互(突出显示文本等)。我尝试过抓取 EnterNotify 上的按钮,然后在窗口升起后立即取消抓取按钮,但这似乎没有效果,窗口管理器的行为就好像我根本没有抓取过按钮一样。

我的程序还比较小,所以代码如下:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "window_manager.h"


ewm_instance wm;

void 
ewm_init()
{
    wm._display = XOpenDisplay(NULL);

    if (!wm._display) {
        printf("Could not open display %s\n", XDisplayName(NULL));
    } 
    wm._root = DefaultRootWindow(wm._display);
}

void
ewm_run()
{
    XSelectInput(wm._display, 
                 wm._root, 
                 SubstructureRedirectMask | SubstructureNotifyMask | 
                 KeyPressMask | KeyReleaseMask | 
                 ButtonPressMask | ButtonReleaseMask);

    XSync(wm._display, 0);
    XGrabServer(wm._display);
    Window returned_root, returned_parent;
    Window *top_level_windows;
    unsigned int num_top_level_windows;
    XQueryTree(wm._display,
               wm._root,
               &returned_root,
               &returned_parent,
               &top_level_windows,
               &num_top_level_windows);
    XFree(top_level_windows);
    XUngrabServer(wm._display);
    XGrabButton(
        wm._display,
        Button1,
        ControlMask,
        wm._root,
        0,
        ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
        GrabModeAsync,
        GrabModeAsync,
        None,
        None);

    XGrabButton(
        wm._display,
        Button1,
        ControlMask,
        wm._root,
        0,
        ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
        GrabModeAsync,
        GrabModeAsync,
        None,
        None);

    for (;;) {
        XEvent e;
        XNextEvent(wm._display, &e);

        switch (e.type) {
        case CreateNotify:
            printf("CreateNotify\n");
            break;
        case DestroyNotify:
            printf("DestroyNotify\n");
            break;
        case ReparentNotify:
            printf("ReparentNotify\n");
            break;
        case MapNotify:
            printf("Mapping Window\n");
            break;
        case UnmapNotify:
            printf("UnmapNotify\n");
            break;
        case ConfigureNotify:
            printf("ConfigureNotify\n");
            break;
        case MapRequest:
            printf("MapRequest\n");
            ewm_on_map_request(&e.xmaprequest);
            break;
        case ConfigureRequest:
            printf("ConfigureRequest\n");
            break;
        case ButtonPress:
            printf("ButtonPress\n");
            ewm_on_button_press(&e.xbutton);
            break;
        case ButtonRelease:
            printf("ButtonRelease\n");
            break;
        case MotionNotify:
            ewm_on_motion_notify(&e.xmotion);
            break;
        case KeyPress:
            printf("KeyPress\n");
            ewm_on_key_press(&e.xkey);
            break;
        case KeyRelease:
            printf("KeyRelease\n");
            break;
        case EnterNotify:
            ewm_on_enter_notify(&e.xcrossing);
            break;
        default:
            printf("Something else\n");
        }
    }
}

void
ewm_on_map_request(const XMapRequestEvent *e)
{
    XSelectInput(
            wm._display,
            e->window,
            KeyPressMask | KeyReleaseMask |
            EnterWindowMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);

    XMapWindow(wm._display, e->window);
    XSetInputFocus(wm._display, e->window, RevertToPointerRoot, CurrentTime);

}

void
ewm_on_enter_notify(const XEnterWindowEvent *e)
{
    printf("Entered window: %lu\n", e->window);
    XSetInputFocus(wm._display, e->window, RevertToParent, CurrentTime);
}


void
ewm_on_key_press(const XKeyEvent *e)
{
    if ((e->state & ControlMask) && 
         e->keycode == XKeysymToKeycode(wm._display, XK_q)) {
        printf("Destroying window\n");
        XDestroyWindow(wm._display, e->window);
    }

    if ((e->state & ControlMask) && 
         e->keycode == XKeysymToKeycode(wm._display, XK_Return)) {
        printf("Enter Works\n");
        system("urxvt &");

    }
}


void
ewm_on_button_press(const XButtonEvent *e)
{
    if (e->subwindow != 0) {

        // Save initial cursor position;
        wm._cursor_start_position = (Vector){e->x_root, e->y_root};

        // Save initial window info
        Window returned_root;
        int x, y;
        unsigned int width, height, depth, border_width;
        XGetGeometry(wm._display,
                     e->subwindow,
                     &returned_root,
                     &x, &y, 
                     &width, &height,
                     &border_width,
                     &depth);
        wm._window_start_position = (Vector){x, y};
        wm._window_start_size = (Size){width, height};

        XRaiseWindow(wm._display, e->subwindow);
        XSetInputFocus(wm._display, e->subwindow, RevertToParent, CurrentTime);
        printf("Raising window %lu\n", e->subwindow);
        printf("root id: %lu\n", wm._root);
        XUngrabButton(wm._display, Button1, AnyModifier, e->subwindow);
    }
}

void
ewm_on_motion_notify(const XMotionEvent *e)
{
    const Vector drag_pos = {e->x_root, e->y_root};
    const Vector delta = {
        (drag_pos.x - wm._cursor_start_position.x), 
        (drag_pos.y - wm._cursor_start_position.y)
    };

    if ((e->state & Button1Mask) && (e->state & ControlMask)) {
        const Vector dest_window_pos = {
            (wm._window_start_position.x + delta.x), 
            (wm._window_start_position.y + delta.y)
        };

        if (e->subwindow != 0) {
            XMoveWindow(wm._display, 
                    e->subwindow, 
                    dest_window_pos.x, 
                    dest_window_pos.y);
        }
    }
}

void 
ewm_cleanup()
{
    XCloseDisplay(wm._display);
}

我也尝试过使用 XSendEvent,但根据我从中得到的结果,我认为我不太明白它应该做什么。我对 Xlib 编程非常陌生,因此非常感谢任何帮助。

谢谢!

c x11 xlib xorg
1个回答
3
投票

我也遇到了完全相同的问题。 原始问题中的评论有所帮助,但还没有完全实现,它们缺少一个重要的细节(前面的第 1 点)。 最终我在这里找到了提示,并解决了它。

  1. 同步抓取事件(注意
    GrabModeSync
    给出为
    pointer_mode
XGrabButton(dis, FOCUS_BUTTON, AnyModifier, root, False, BUTTONMASK, GrabModeSync, GrabModeAsync, None, None);
  1. 使用
    XAllowEvents
    XSync
    实现直通效果:
XAllowEvents(display, ReplayPointer, ev->xbutton.time);
XSync(display, 0);
© www.soinside.com 2019 - 2024. All rights reserved.