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. Change DRAG_CANCEL to DRAG_REVERT to clarify the distinction between "abort" and "revert/cancel" actions. 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/294/raw.patch | git am
b/include/floating.h
| 29 |
@@ -134,12 +134,24 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace); |
| 30 |
|
| 31 |
#endif |
| 32 |
/** |
| 33 |
- * This is the return value of a drag operation like drag_pointer. DRAG_CANCEL |
| 34 |
- * will indicate the intention of the drag should not be carried out, or that |
| 35 |
- * the drag actions should be undone. |
| 36 |
+ * This is the return value of a drag operation like drag_pointer. |
| 37 |
+ * |
| 38 |
+ * DRAG_SUCCESS will indicate the intention of the drag action should be |
| 39 |
+ * carried out. |
| 40 |
+ * |
| 41 |
+ * DRAG_REVERT will indicate an attempt should be made to restore the state of |
| 42 |
+ * the involved windows to their condition before the drag. |
| 43 |
+ * |
| 44 |
+ * DRAG_ABORT will indicate that the intention of the drag action cannot be |
| 45 |
+ * carried out (e.g. because the window has been unmapped). |
| 46 |
* |
| 47 |
*/ |
| 48 |
-typedef enum { DRAG_SUCCESS = 0, DRAG_CANCEL } drag_result_t;
|
| 49 |
+typedef enum {
|
| 50 |
+ DRAGGING = 0, |
| 51 |
+ DRAG_SUCCESS, |
| 52 |
+ DRAG_REVERT, |
| 53 |
+ DRAG_ABORT |
| 54 |
+} drag_result_t; |
| 55 |
|
| 56 |
/** |
| 57 |
* This function grabs your pointer and keyboard and lets you drag stuff around |
b/src/floating.c
| 62 |
@@ -441,14 +441,14 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event) {
|
| 63 |
* after the user releases the mouse button */ |
| 64 |
tree_render(); |
| 65 |
|
| 66 |
- /* Store the initial rect in case of user cancel */ |
| 67 |
+ /* Store the initial rect in case of user revert/cancel */ |
| 68 |
Rect initial_rect = con->rect; |
| 69 |
|
| 70 |
/* Drag the window */ |
| 71 |
drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event); |
| 72 |
|
| 73 |
/* If the user cancelled, undo the changes. */ |
| 74 |
- if (drag_result == DRAG_CANCEL) |
| 75 |
+ if (drag_result == DRAG_REVERT) |
| 76 |
floating_reposition(con, initial_rect); |
| 77 |
|
| 78 |
/* If this is a scratchpad window, don't auto center it from now on. */ |
| 79 |
@@ -553,13 +553,13 @@ void floating_resize_window(Con *con, const bool proportional, |
| 80 |
|
| 81 |
struct resize_window_callback_params params = { corner, proportional, event };
|
| 82 |
|
| 83 |
- /* get the initial rect in case of cancel */ |
| 84 |
+ /* get the initial rect in case of revert/cancel */ |
| 85 |
Rect initial_rect = con->rect; |
| 86 |
|
| 87 |
drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, ¶ms); |
| 88 |
|
| 89 |
/* If the user cancels, undo the resize */ |
| 90 |
- if (drag_result == DRAG_CANCEL) |
| 91 |
+ if (drag_result == DRAG_REVERT) |
| 92 |
floating_reposition(con, initial_rect); |
| 93 |
|
| 94 |
/* If this is a scratchpad window, don't auto center it from now on. */ |
| 95 |
@@ -601,7 +601,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 96 |
|
| 97 |
if ((reply = xcb_grab_pointer_reply(conn, cookie, NULL)) == NULL) {
|
| 98 |
ELOG("Could not grab pointer\n");
|
| 99 |
- return DRAG_CANCEL; |
| 100 |
+ return DRAG_ABORT; |
| 101 |
} |
| 102 |
|
| 103 |
free(reply); |
| 104 |
@@ -620,7 +620,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 105 |
|
| 106 |
if ((keyb_reply = xcb_grab_keyboard_reply(conn, keyb_cookie, NULL)) == NULL) {
|
| 107 |
ELOG("Could not grab keyboard\n");
|
| 108 |
- return DRAG_CANCEL; |
| 109 |
+ return DRAG_ABORT; |
| 110 |
} |
| 111 |
|
| 112 |
free(keyb_reply); |
| 113 |
@@ -629,11 +629,11 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 114 |
xcb_flush(conn); |
| 115 |
|
| 116 |
xcb_generic_event_t *inside_event, *last_motion_notify = NULL; |
| 117 |
- bool loop_done = false; |
| 118 |
- /* The return value, set to DRAG_CANCEL on user cancel */ |
| 119 |
- drag_result_t drag_result = DRAG_SUCCESS; |
| 120 |
+ Con *inside_con = NULL; |
| 121 |
+ /* The return value, set to exit the event loop */ |
| 122 |
+ drag_result_t drag_result = DRAGGING; |
| 123 |
/* I’ve always wanted to have my own eventhandler… */ |
| 124 |
- while (!loop_done && (inside_event = xcb_wait_for_event(conn))) {
|
| 125 |
+ while (drag_result == DRAGGING && (inside_event = xcb_wait_for_event(conn))) {
|
| 126 |
/* We now handle all events we can get using xcb_poll_for_event */ |
| 127 |
do {
|
| 128 |
/* skip x11 errors */ |
| 129 |
@@ -646,7 +646,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 130 |
|
| 131 |
switch (type) {
|
| 132 |
case XCB_BUTTON_RELEASE: |
| 133 |
- loop_done = true; |
| 134 |
+ drag_result = DRAG_SUCCESS; |
| 135 |
break; |
| 136 |
|
| 137 |
case XCB_MOTION_NOTIFY: |
| 138 |
@@ -656,17 +656,18 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 139 |
break; |
| 140 |
|
| 141 |
case XCB_UNMAP_NOTIFY: |
| 142 |
- DLOG("Unmap-notify, aborting\n");
|
| 143 |
+ inside_con = con_by_window_id(((xcb_unmap_notify_event_t*)inside_event)->window); |
| 144 |
+ |
| 145 |
+ DLOG("UnmapNotify for window 0x%08x (container %p)\n", ((xcb_unmap_notify_event_t*)inside_event)->window, inside_con);
|
| 146 |
+ drag_result = DRAG_ABORT; |
| 147 |
+ |
| 148 |
handle_event(type, inside_event); |
| 149 |
- loop_done = true; |
| 150 |
- drag_result = DRAG_CANCEL; |
| 151 |
break; |
| 152 |
|
| 153 |
case XCB_KEY_PRESS: |
| 154 |
/* Cancel the drag if a key was pressed */ |
| 155 |
- DLOG("A key was pressed during drag, canceling.");
|
| 156 |
- loop_done = true; |
| 157 |
- drag_result = DRAG_CANCEL; |
| 158 |
+ DLOG("A key was pressed during drag, reverting changes.");
|
| 159 |
+ drag_result = DRAG_REVERT; |
| 160 |
|
| 161 |
handle_event(type, inside_event); |
| 162 |
break; |
| 163 |
@@ -681,7 +682,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_ |
| 164 |
free(inside_event); |
| 165 |
} while ((inside_event = xcb_poll_for_event(conn)) != NULL); |
| 166 |
|
| 167 |
- if (last_motion_notify == NULL || loop_done) |
| 168 |
+ if (last_motion_notify == NULL || drag_result != DRAGGING) |
| 169 |
continue; |
| 170 |
|
| 171 |
new_x = ((xcb_motion_notify_event_t*)last_motion_notify)->root_x; |
b/src/resize.c
| 176 |
@@ -161,7 +161,7 @@ int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, |
| 177 |
xcb_flush(conn); |
| 178 |
|
| 179 |
/* User cancelled the drag so no action should be taken. */ |
| 180 |
- if (drag_result == DRAG_CANCEL) |
| 181 |
+ if (drag_result == DRAG_REVERT) |
| 182 |
return 0; |
| 183 |
|
| 184 |
int pixels; |