i3 - improved tiling WM


Return DRAG_ABORT on UnmapNotify from drag_pointer

Patch status: needinfo

Patch by Tony Crisci

Long description:

Add DRAG_ABORT to enum drag_result_t. DRAG_ABORT will indicate the drag
operation cannot be completed.

Return DRAG_ABORT on UnmapNotify, or when the keyboard or pointer cannot
be grabbed.

Add DRAGGING to return value for drag_result_t. DRAGGING is used
internally by drag_pointer to indicate the drag is in progress.

Fixes an issue that caused i3 to crash when a user is dragging or
resizing a floating window that becomes destroyed.

To apply this patch, use:
curl http://cr.i3wm.org/patch/291/raw.patch | git am

b/include/floating.h

25
@@ -134,12 +134,25 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace);
26
 
27
 #endif
28
 /**
29
- * This is the return value of a drag operation like drag_pointer. DRAG_CANCEL
30
- * will indicate the intention of the drag should not be carried out, or that
31
- * the drag actions should be undone.
32
+ * This is the return value of a drag operation like drag_pointer.
33
+ *
34
+ * DRAG_SUCCESS will indicate the intention of the drag action should be
35
+ * carried out.
36
+ *
37
+ * DRAG_CANCEL will indicate that the drag was stopped by a user action, and an
38
+ * attempt should be made to restore the state of the involved windows to their
39
+ * condition before the drag.
40
+ *
41
+ * DRAG_ABORT will indicate that the intention of the drag action cannot be
42
+ * carried out (e.g. because the window has been unmapped).
43
  *
44
  */
45
-typedef enum { DRAG_SUCCESS = 0, DRAG_CANCEL } drag_result_t;
46
+typedef enum {
47
+    DRAGGING = 0,
48
+    DRAG_SUCCESS,
49
+    DRAG_CANCEL,
50
+    DRAG_ABORT
51
+} drag_result_t;
52
 
53
 /**
54
  * This function grabs your pointer and keyboard and lets you drag stuff around

b/src/floating.c

59
@@ -601,7 +601,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
60
 
61
     if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) {
62
         ELOG("Could not grab pointer\n");
63
-        return DRAG_CANCEL;
64
+        return DRAG_ABORT;
65
     }
66
 
67
     free(reply);
68
@@ -620,7 +620,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
69
 
70
     if ((keyb_reply = xcb_grab_keyboard_reply(conn, keyb_cookie, NULL)) == NULL) {
71
         ELOG("Could not grab keyboard\n");
72
-        return DRAG_CANCEL;
73
+        return DRAG_ABORT;
74
     }
75
 
76
     free(keyb_reply);
77
@@ -629,11 +629,11 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
78
     xcb_flush(conn);
79
 
80
     xcb_generic_event_t *inside_event, *last_motion_notify = NULL;
81
-    bool loop_done = false;
82
-    /* The return value, set to DRAG_CANCEL on user cancel */
83
-    drag_result_t drag_result = DRAG_SUCCESS;
84
+    Con *inside_con = NULL;
85
+    /* The return value, set to exit the event loop */
86
+    drag_result_t drag_result = DRAGGING;
87
     /* I’ve always wanted to have my own eventhandler… */
88
-    while (!loop_done && (inside_event = xcb_wait_for_event(conn))) {
89
+    while (drag_result == DRAGGING && (inside_event = xcb_wait_for_event(conn))) {
90
         /* We now handle all events we can get using xcb_poll_for_event */
91
         do {
92
             /* skip x11 errors */
93
@@ -646,7 +646,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
94
 
95
             switch (type) {
96
                 case XCB_BUTTON_RELEASE:
97
-                    loop_done = true;
98
+                    drag_result = DRAG_SUCCESS;
99
                     break;
100
 
101
                 case XCB_MOTION_NOTIFY:
102
@@ -656,16 +656,18 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
103
                     break;
104
 
105
                 case XCB_UNMAP_NOTIFY:
106
-                    DLOG("Unmap-notify, aborting\n");
107
+                    inside_con = con_by_window_id(((xcb_unmap_notify_event_t*)inside_event)->window);
108
+
109
+                    DLOG("UnmapNotify for window 0x%08x (container %p)\n", ((xcb_unmap_notify_event_t*)inside_event)->window, inside_con);
110
+                    DLOG("UnmapNotify, aborting\n");
111
+                    drag_result = DRAG_ABORT;
112
+
113
                     handle_event(type, inside_event);
114
-                    loop_done = true;
115
-                    drag_result = DRAG_CANCEL;
116
                     break;
117
 
118
                 case XCB_KEY_PRESS:
119
                     /* Cancel the drag if a key was pressed */
120
                     DLOG("A key was pressed during drag, canceling.");
121
-                    loop_done = true;
122
                     drag_result = DRAG_CANCEL;
123
 
124
                     handle_event(type, inside_event);
125
@@ -681,7 +683,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
126
                 free(inside_event);
127
         } while ((inside_event = xcb_poll_for_event(conn)) != NULL);
128
 
129
-        if (last_motion_notify == NULL || loop_done)
130
+        if (last_motion_notify == NULL || drag_result != DRAGGING)
131
             continue;
132
 
133
         new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x;