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

@ -567,6 +567,104 @@ void floating_resize_window(Con *con, const bool proportional,
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
* (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
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) ?
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);
/* 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;
Con *inside_con = NULL;
while (loop.result == DRAGGING)
ev_run(main_loop, EVRUN_ONCE);
drag_result_t drag_result = DRAGGING;
/* Ive always wanted to have my own eventhandler… */
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);
}
ev_check_stop(main_loop, &loop.check);
main_set_x11_cb(true);
xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME);
xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
xcb_flush(conn);
return drag_result;
return loop.result;
}
/*