]> git.sur5r.net Git - i3/i3/commitdiff
dragging: instead of using a custom event loop, use libev
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 15 Dec 2013 16:30:06 +0000 (17:30 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 22 Dec 2013 20:52:49 +0000 (21:52 +0100)
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.

include/all.h
include/main.h [new file with mode: 0644]
src/floating.c
src/main.c
src/restore_layout.c

index 840ca44364aed97e6533f1024f2f5961267f3699..9ba4c1d9330842764e555c1343cd46cbd449dd8a 100644 (file)
@@ -84,5 +84,6 @@
 #include "fake_outputs.h"
 #include "display_version.h"
 #include "restore_layout.h"
+#include "main.h"
 
 #endif
diff --git a/include/main.h b/include/main.h
new file mode 100644 (file)
index 0000000..9261a5d
--- /dev/null
@@ -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
index 10962403b8fa09775f210c8951391029a43ef940..166e054cf67f93942013a0c1f3f6092971db4129 100644 (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);
-
-    xcb_generic_event_t *inside_event, *last_motion_notify = NULL;
-    Con *inside_con = NULL;
-
-    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);
-    }
+    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);
+
+    while (loop.result == DRAGGING)
+        ev_run(main_loop, EVRUN_ONCE);
+
+    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;
 }
 
 /*
index c8cfedd75d225138b2ba009e20f56086a4fc8237..ca2617c6d5e0b620568b3a96b0ffcf663f39a60f 100644 (file)
@@ -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);
index 96e608400adf7c8263cf36905475d8494988e889..cdf15517e10a68e65b97680caa34f31b6796ddb2 100644 (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);
 }
 
-// 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: