Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture Button Events in Xlib then passing the Event to the client

Tags:

c

x11

xorg

xlib

I'm working on a window manager, mainly as an exercise, and I'm facing a problem. I would like to be able to raise the clicked window to the top of the stack. Currently, I am using XGrabButton on Button1 and ControlMask to allow for moving of windows, and when I Ctrl+click the window, the desired effect is achieved. However, if I use XGrabButton on Button1 with AnyModifier, while the effect I am looking for is achieved, I can no longer interact with the client window (highlighting text, etc.) through the mouse button. I have tried Grabbing the button on EnterNotify, then ungrabbing the button as soon as the window is raised, but this appears to have no effect and the window manager acts as though I never grabbed the button at all.

My program is still relatively small, so here is the code:

#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);
}

I have also tried to use XSendEvent, but based on the results I have gotten from it I don't think I understand what it is supposed to do very well. I am very new to Xlib programming so any help is greatly appreciated.

Thank you!

like image 617
EthanS Avatar asked Sep 18 '17 21:09

EthanS


1 Answers

I had exactly the same problem. The comments in the original question helped, but they're not quite there yet, they're missing an important detail (point 1 ahead). Ultimately I found the hint here solved it.

  1. Grab the event synchronously (notice GrabModeSync given as pointer_mode)
XGrabButton(dis, FOCUS_BUTTON, AnyModifier, root, False, BUTTONMASK, GrabModeSync, GrabModeAsync, None, None);
  1. Use XAllowEvents and Xsync for the pass-through effect
XAllowEvents(display, ReplayPointer, ev->xbutton.time);
XSync(display, 0);
like image 187
Joao Tavora Avatar answered Oct 22 '22 03:10

Joao Tavora