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 "fake_outputs.h"
|
||||||
#include "display_version.h"
|
#include "display_version.h"
|
||||||
#include "restore_layout.h"
|
#include "restore_layout.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
#endif
|
#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;
|
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);
|
||||||
/* I’ve 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
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. */
|
/** 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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue