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:
parent
f57f94c850
commit
e567cf436c
|
@ -84,5 +84,6 @@
|
|||
#include "fake_outputs.h"
|
||||
#include "display_version.h"
|
||||
#include "restore_layout.h"
|
||||
#include "main.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
190
src/floating.c
190
src/floating.c
|
@ -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;
|
||||
/* I’ve 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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
23
src/main.c
23
src/main.c
|
@ -31,6 +31,10 @@ struct rlimit original_rlimit_core;
|
|||
/** The number of file descriptors passed via socket activation. */
|
||||
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;
|
||||
|
||||
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
|
||||
|
@ -742,7 +763,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
struct ev_io *xcb_watcher = 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));
|
||||
|
||||
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
switch (type) {
|
||||
case XCB_EXPOSE:
|
||||
|
|
Loading…
Reference in New Issue