dragging: instead of using a custom event loop, use libev

This is done by installing a new check watcher that replaces the main
X11 event handler and calling ev_run with EVRUN_ONCE until the dragging
loop left state DRAGGING.

With this commit, other handlers, most notably the redraw handler for
placeholder windows, get a chance to run when dragging (placeholder!)
windows around.
This commit is contained in:
Michael Stapelberg 2013-12-15 17:30:06 +01:00
parent f57f94c850
commit e567cf436c
5 changed files with 158 additions and 78 deletions

View File

@ -84,5 +84,6 @@
#include "fake_outputs.h" #include "fake_outputs.h"
#include "display_version.h" #include "display_version.h"
#include "restore_layout.h" #include "restore_layout.h"
#include "main.h"
#endif #endif

21
include/main.h Normal file
View File

@ -0,0 +1,21 @@
/*
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
*
* main.c: Initialization, main loop
*
*/
#ifndef I3_MAIN_H
#define I3_MAIN_H
/**
* Enable or disable the main X11 event handling function.
* This is used by drag_pointer() which has its own, modal event handler, which
* takes precedence over the normal event handler.
*
*/
void main_set_x11_cb(bool enable);
#endif

View File

@ -567,6 +567,104 @@ void floating_resize_window(Con *con, const bool proportional,
con->scratchpad_state = SCRATCHPAD_CHANGED; con->scratchpad_state = SCRATCHPAD_CHANGED;
} }
/* As endorsed by “ASSOCIATING CUSTOM DATA WITH A WATCHER” in ev(3) */
struct drag_x11_cb {
ev_check check;
/* Whether this modal event loop should be exited and with which result. */
drag_result_t result;
/* The container that is being dragged or resized, or NULL if this is a
* drag of the resize handle. */
Con *con;
/* The dimensions of con when the loop was started. */
Rect old_rect;
/* The callback to invoke after every pointer movement. */
callback_t callback;
/* User data pointer for callback. */
const void *extra;
};
static void xcb_drag_check_cb(EV_P_ ev_check *w, int revents) {
struct drag_x11_cb *dragloop = (struct drag_x11_cb*)w;
xcb_motion_notify_event_t *last_motion_notify = NULL;
xcb_generic_event_t *event;
while ((event = xcb_poll_for_event(conn)) != NULL) {
if (event->response_type == 0) {
xcb_generic_error_t *error = (xcb_generic_error_t*)event;
DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n",
error->sequence, error->error_code);
free(event);
continue;
}
/* Strip off the highest bit (set if the event is generated) */
int type = (event->response_type & 0x7F);
switch (type) {
case XCB_BUTTON_RELEASE:
dragloop->result = DRAG_SUCCESS;
break;
case XCB_KEY_PRESS:
DLOG("A key was pressed during drag, reverting changes.");
dragloop->result = DRAG_REVERT;
handle_event(type, event);
break;
case XCB_UNMAP_NOTIFY: {
xcb_unmap_notify_event_t *unmap_event = (xcb_unmap_notify_event_t*)event;
Con *con = con_by_window_id(unmap_event->window);
if (con != NULL) {
DLOG("UnmapNotify for window 0x%08x (container %p)\n", unmap_event->window, con);
if (con_get_workspace(con) == con_get_workspace(focused)) {
DLOG("UnmapNotify for a managed window on the current workspace, aborting\n");
dragloop->result = DRAG_ABORT;
}
}
handle_event(type, event);
break;
}
case XCB_MOTION_NOTIFY:
/* motion_notify events are saved for later */
FREE(last_motion_notify);
last_motion_notify = (xcb_motion_notify_event_t*)event;
break;
default:
DLOG("Passing to original handler\n");
handle_event(type, event);
break;
}
if (last_motion_notify != (xcb_motion_notify_event_t*)event)
free(event);
if (dragloop->result != DRAGGING)
return;
}
if (last_motion_notify == NULL)
return;
dragloop->callback(
dragloop->con,
&(dragloop->old_rect),
last_motion_notify->root_x,
last_motion_notify->root_y,
dragloop->extra);
free(last_motion_notify);
}
/* /*
* This function grabs your pointer and keyboard and lets you drag stuff around * This function grabs your pointer and keyboard and lets you drag stuff around
* (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will
@ -578,11 +676,6 @@ void floating_resize_window(Con *con, const bool proportional,
drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
confine_to, border_t border, int cursor, callback_t callback, const void *extra) confine_to, border_t border, int cursor, callback_t callback, const void *extra)
{ {
uint32_t new_x, new_y;
Rect old_rect = { 0, 0, 0, 0 };
if (con != NULL)
memcpy(&old_rect, &(con->rect), sizeof(Rect));
xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcb_cursor_t xcursor = (cursor && xcursor_supported) ?
xcursor_get_cursor(cursor) : XCB_NONE; xcursor_get_cursor(cursor) : XCB_NONE;
@ -626,84 +719,29 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
free(keyb_reply); free(keyb_reply);
/* Go into our own event loop */ /* Go into our own event loop */
xcb_flush(conn); struct drag_x11_cb loop = {
.result = DRAGGING,
.con = con,
.callback = callback,
.extra = extra,
};
if (con)
loop.old_rect = con->rect;
ev_check_init(&loop.check, xcb_drag_check_cb);
main_set_x11_cb(false);
ev_check_start(main_loop, &loop.check);
xcb_generic_event_t *inside_event, *last_motion_notify = NULL; while (loop.result == DRAGGING)
Con *inside_con = NULL; ev_run(main_loop, EVRUN_ONCE);
drag_result_t drag_result = DRAGGING; ev_check_stop(main_loop, &loop.check);
/* Ive always wanted to have my own eventhandler… */ main_set_x11_cb(true);
while (drag_result == DRAGGING && (inside_event = xcb_wait_for_event(conn))) {
/* We now handle all events we can get using xcb_poll_for_event */
do {
/* skip x11 errors */
if (inside_event->response_type == 0) {
free(inside_event);
continue;
}
/* Strip off the highest bit (set if the event is generated) */
int type = (inside_event->response_type & 0x7F);
switch (type) {
case XCB_BUTTON_RELEASE:
drag_result = DRAG_SUCCESS;
break;
case XCB_MOTION_NOTIFY:
/* motion_notify events are saved for later */
FREE(last_motion_notify);
last_motion_notify = inside_event;
break;
case XCB_UNMAP_NOTIFY:
inside_con = con_by_window_id(((xcb_unmap_notify_event_t*)inside_event)->window);
if (inside_con != NULL) {
DLOG("UnmapNotify for window 0x%08x (container %p)\n", ((xcb_unmap_notify_event_t*)inside_event)->window, inside_con);
if (con_get_workspace(inside_con) == con_get_workspace(focused)) {
DLOG("UnmapNotify for a managed window on the current workspace, aborting\n");
drag_result = DRAG_ABORT;
}
}
handle_event(type, inside_event);
break;
case XCB_KEY_PRESS:
/* Cancel the drag if a key was pressed */
DLOG("A key was pressed during drag, reverting changes.");
drag_result = DRAG_REVERT;
handle_event(type, inside_event);
break;
default:
DLOG("Passing to original handler\n");
/* Use original handler */
handle_event(type, inside_event);
break;
}
if (last_motion_notify != inside_event)
free(inside_event);
} while ((inside_event = xcb_poll_for_event(conn)) != NULL);
if (last_motion_notify == NULL || drag_result != DRAGGING)
continue;
new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;
new_y = ((xcb_motion_notify_event_t*)last_motion_notify)->root_y;
callback(con, &old_rect, new_x, new_y, extra);
FREE(last_motion_notify);
}
xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME); xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME);
xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
xcb_flush(conn); xcb_flush(conn);
return drag_result; return loop.result;
} }
/* /*

View File

@ -31,6 +31,10 @@ struct rlimit original_rlimit_core;
/** The number of file descriptors passed via socket activation. */ /** The number of file descriptors passed via socket activation. */
int listen_fds; int listen_fds;
/* We keep the xcb_check watcher around to be able to enable and disable it
* temporarily for drag_pointer(). */
static struct ev_check *xcb_check;
static int xkb_event_base; static int xkb_event_base;
int xkb_current_group; int xkb_current_group;
@ -143,6 +147,23 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
} }
} }
/*
* Enable or disable the main X11 event handling function.
* This is used by drag_pointer() which has its own, modal event handler, which
* takes precedence over the normal event handler.
*
*/
void main_set_x11_cb(bool enable) {
DLOG("Setting main X11 callback to enabled=%d\n", enable);
if (enable) {
ev_check_start(main_loop, xcb_check);
/* Trigger the watcher explicitly to handle all remaining X11 events.
* drag_pointer()s event handler exits in the middle of the loop. */
ev_feed_event(main_loop, xcb_check, 0);
} else {
ev_check_stop(main_loop, xcb_check);
}
}
/* /*
* When using xmodmap to change the keyboard mapping, this event * When using xmodmap to change the keyboard mapping, this event
@ -742,7 +763,7 @@ int main(int argc, char *argv[]) {
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
struct ev_io *xkb = scalloc(sizeof(struct ev_io)); struct ev_io *xkb = scalloc(sizeof(struct ev_io));
struct ev_check *xcb_check = scalloc(sizeof(struct ev_check)); xcb_check = scalloc(sizeof(struct ev_check));
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);

View File

@ -335,7 +335,6 @@ static void configure_notify(xcb_configure_notify_event_t *event) {
ELOG("Received ConfigureNotify for unknown window 0x%08x\n", event->window); ELOG("Received ConfigureNotify for unknown window 0x%08x\n", event->window);
} }
// TODO: this event loop is not taken care of in the floating event handler
static void restore_handle_event(int type, xcb_generic_event_t *event) { static void restore_handle_event(int type, xcb_generic_event_t *event) {
switch (type) { switch (type) {
case XCB_EXPOSE: case XCB_EXPOSE: