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!
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.
GrabModeSync
given as pointer_mode
)XGrabButton(dis, FOCUS_BUTTON, AnyModifier, root, False, BUTTONMASK, GrabModeSync, GrabModeAsync, None, None);
XAllowEvents
and Xsync
for the pass-through effectXAllowEvents(display, ReplayPointer, ev->xbutton.time);
XSync(display, 0);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With