i3 - improved tiling WM


Suppport _NET_WM_MOVERESIZE completely

Patch status: superseded

Patch by Lukas K

Long description:

fixes #1432
This patch supersedes the previous one regarding _NET_WM_MOVERESIZE.
It adds support for resizing windows using _NET_WM_MOVERESIZE_SIZE and
fixes the number of supported atoms added to the root window.

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

b/include/atoms.xmacro

24
@@ -1,6 +1,7 @@
25
 xmacro(_NET_SUPPORTED)
26
 xmacro(_NET_SUPPORTING_WM_CHECK)
27
 xmacro(_NET_WM_NAME)
28
+xmacro(_NET_WM_MOVERESIZE)
29
 xmacro(_NET_WM_STATE_FULLSCREEN)
30
 xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
31
 xmacro(_NET_WM_STATE_MODAL)

b/include/floating.h

36
@@ -96,7 +96,7 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event);
37
  * Calls the drag_pointer function with the resize_window callback
38
  *
39
  */
40
-void floating_resize_window(Con *con, const bool proportional, const xcb_button_press_event_t *event);
41
+void floating_resize_window(Con *con, const bool proportional, const xcb_button_press_event_t *event, border_t force_corner);
42
 
43
 /**
44
  * Called when a floating window is created or resized.

b/include/xcursor.h

49
@@ -19,6 +19,10 @@ enum xcursor_cursor_t {
50
     XCURSOR_CURSOR_TOP_RIGHT_CORNER,
51
     XCURSOR_CURSOR_BOTTOM_LEFT_CORNER,
52
     XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER,
53
+    XCURSOR_CURSOR_TOP_SIDE,
54
+    XCURSOR_CURSOR_RIGHT_SIDE,
55
+    XCURSOR_CURSOR_BOTTOM_SIDE,
56
+    XCURSOR_CURSOR_LEFT_SIDE,
57
     XCURSOR_CURSOR_WATCH,
58
     XCURSOR_CURSOR_MOVE,
59
     XCURSOR_CURSOR_MAX

b/src/click.c

64
@@ -266,7 +266,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
65
          * also try resizing (tiling) if it was a click on the top */
66
         if (mod_pressed && event->detail == XCB_BUTTON_INDEX_3) {
67
             DLOG("floating resize due to floatingmodifier\n");
68
-            floating_resize_window(floatingcon, proportional, event);
69
+            floating_resize_window(floatingcon, proportional, event, 0);
70
             return 1;
71
         }
72
 
73
@@ -279,13 +279,13 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
74
 
75
         if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_INDEX_3) {
76
             DLOG("floating resize due to decoration right click\n");
77
-            floating_resize_window(floatingcon, proportional, event);
78
+            floating_resize_window(floatingcon, proportional, event, 0);
79
             return 1;
80
         }
81
 
82
         if (dest == CLICK_BORDER) {
83
             DLOG("floating resize due to border click\n");
84
-            floating_resize_window(floatingcon, proportional, event);
85
+            floating_resize_window(floatingcon, proportional, event, 0);
86
             return 1;
87
         }
88
 

b/src/ewmh.c

93
@@ -234,5 +234,6 @@ void ewmh_setup_hints(void) {
94
     /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
95
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
96
 
97
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 23, supported_atoms);
98
+    /* only send the first 24 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
99
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 24, supported_atoms);
100
 }

b/src/floating.c

105
@@ -488,8 +488,8 @@ DRAGGING_CB(resize_window_callback) {
106
 
107
     int32_t dest_x = con->rect.x;
108
     int32_t dest_y = con->rect.y;
109
-    uint32_t dest_width;
110
-    uint32_t dest_height;
111
+    uint32_t dest_width = old_rect->width;
112
+    uint32_t dest_height = old_rect->height;
113
 
114
     double ratio = (double)old_rect->width / old_rect->height;
115
 
116
@@ -497,12 +497,12 @@ DRAGGING_CB(resize_window_callback) {
117
      * taking into account in which corner the client was grabbed */
118
     if (corner & BORDER_LEFT)
119
         dest_width = old_rect->width - (new_x - event->root_x);
120
-    else
121
+    else if (corner & BORDER_RIGHT)
122
         dest_width = old_rect->width + (new_x - event->root_x);
123
 
124
     if (corner & BORDER_TOP)
125
         dest_height = old_rect->height - (new_y - event->root_y);
126
-    else
127
+    else if (corner & BORDER_BOTTOM)
128
         dest_height = old_rect->height + (new_y - event->root_y);
129
 
130
     /* User wants to keep proportions, so we may have to adjust our values */
131
@@ -540,26 +540,41 @@ DRAGGING_CB(resize_window_callback) {
132
  *
133
  */
134
 void floating_resize_window(Con *con, const bool proportional,
135
-                            const xcb_button_press_event_t *event) {
136
+                            const xcb_button_press_event_t *event,
137
+                            border_t force_corner) {
138
     DLOG("floating_resize_window\n");
139
 
140
     /* corner saves the nearest corner to the original click. It contains
141
      * a bitmask of the nearest borders (BORDER_LEFT, BORDER_RIGHT, …) */
142
     border_t corner = 0;
143
+    int cursor = 0;
144
+    if(force_corner == 0) {
145
+        if (event->event_x <= (int16_t)(con->rect.width / 2))
146
+            corner |= BORDER_LEFT;
147
+        else
148
+            corner |= BORDER_RIGHT;
149
 
150
-    if (event->event_x <= (int16_t)(con->rect.width / 2))
151
-        corner |= BORDER_LEFT;
152
-    else
153
-        corner |= BORDER_RIGHT;
154
+        if (event->event_y <= (int16_t)(con->rect.height / 2))
155
+            corner |= BORDER_TOP;
156
+        else
157
+            corner |= BORDER_BOTTOM;
158
+    }
159
+    else {
160
+        corner = force_corner;
161
+    }
162
 
163
-    int cursor = 0;
164
-    if (event->event_y <= (int16_t)(con->rect.height / 2)) {
165
-        corner |= BORDER_TOP;
166
+    if(corner == BORDER_TOP)
167
+        cursor = XCURSOR_CURSOR_TOP_SIDE;
168
+    else if(corner == BORDER_RIGHT)
169
+        cursor = XCURSOR_CURSOR_RIGHT_SIDE;
170
+    else if(corner == BORDER_BOTTOM)
171
+        cursor = XCURSOR_CURSOR_BOTTOM_SIDE;
172
+    else if(corner == BORDER_LEFT)
173
+        cursor = XCURSOR_CURSOR_LEFT_SIDE;
174
+    else if(corner & BORDER_TOP)
175
         cursor = (corner & BORDER_LEFT) ? XCURSOR_CURSOR_TOP_LEFT_CORNER : XCURSOR_CURSOR_TOP_RIGHT_CORNER;
176
-    } else {
177
-        corner |= BORDER_BOTTOM;
178
+    else if(corner & BORDER_BOTTOM)
179
         cursor = (corner & BORDER_LEFT) ? XCURSOR_CURSOR_BOTTOM_LEFT_CORNER : XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER;
180
-    }
181
 
182
     struct resize_window_callback_params params = {corner, proportional, event};
183
 

b/src/handlers.c

188
@@ -651,6 +651,20 @@ static void handle_expose_event(xcb_expose_event_t *event) {
189
     return;
190
 }
191
 
192
+
193
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
194
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
195
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
196
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
197
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
198
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
199
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
200
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
201
+#define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
202
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
203
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
204
+#define _NET_WM_MOVERESIZE_CANCEL           11   /* cancel operation */
205
+
206
 /*
207
  * Handle client messages (EWMH)
208
  *
209
@@ -856,6 +870,53 @@ static void handle_client_message(xcb_client_message_event_t *event) {
210
         } else {
211
             DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
212
         }
213
+    }
214
+    else if (event->type == A__NET_WM_MOVERESIZE) {
215
+        /*
216
+         * Client-side decorated Gtk3 windows emit this signal when being
217
+         * dragged by their GtkHeaderBar
218
+         */
219
+         Con *con = con_by_window_id(event->window);
220
+         if (con && con_is_floating(con)) {
221
+             DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con);
222
+             uint32_t direction = event->data.data32[2];
223
+             uint32_t x_root = event->data.data32[0];
224
+             uint32_t y_root = event->data.data32[1];
225
+             /* construct fake xcb_button_press_event_t */
226
+             xcb_button_press_event_t fake = {
227
+                 .root_x = x_root,
228
+                 .root_y = y_root
229
+             };
230
+             if(direction == _NET_WM_MOVERESIZE_MOVE) {
231
+                 floating_drag_window(con->parent, &fake);
232
+
233
+             } else if(direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
234
+                 border_t border=0;
235
+                 if(direction == _NET_WM_MOVERESIZE_SIZE_TOP ||
236
+                    direction == _NET_WM_MOVERESIZE_SIZE_TOPLEFT ||
237
+                    direction == _NET_WM_MOVERESIZE_SIZE_TOPRIGHT)
238
+                        border |= BORDER_TOP;
239
+                 if(direction == _NET_WM_MOVERESIZE_SIZE_RIGHT ||
240
+                         direction == _NET_WM_MOVERESIZE_SIZE_TOPRIGHT ||
241
+                         direction == _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)
242
+                        border |= BORDER_RIGHT;
243
+                 if(direction == _NET_WM_MOVERESIZE_SIZE_BOTTOM ||
244
+                         direction == _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT ||
245
+                         direction == _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)
246
+                        border |= BORDER_BOTTOM;
247
+                 if(direction == _NET_WM_MOVERESIZE_SIZE_LEFT ||
248
+                         direction == _NET_WM_MOVERESIZE_SIZE_TOPLEFT ||
249
+                         direction == _NET_WM_MOVERESIZE_SIZE_TOPRIGHT)
250
+                        border |= BORDER_RIGHT;
251
+
252
+                 floating_resize_window(con->parent, FALSE, &fake, border);
253
+             } else {
254
+                DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
255
+             }
256
+
257
+         } else {
258
+             DLOG("Couldn't find con for _NET_WM_MOVERESIZE request. or con not floating (window = %d)\n", event->window);
259
+         }
260
     } else {
261
         DLOG("unhandled clientmessage\n");
262
         return;

b/src/xcursor.c

267
@@ -44,6 +44,10 @@ void xcursor_load_cursors(void) {
268
     LOAD_CURSOR(XCURSOR_CURSOR_TOP_RIGHT_CORNER, "top_right_corner");
269
     LOAD_CURSOR(XCURSOR_CURSOR_BOTTOM_LEFT_CORNER, "bottom_left_corner");
270
     LOAD_CURSOR(XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER, "bottom_right_corner");
271
+    LOAD_CURSOR(XCURSOR_CURSOR_TOP_SIDE, "top_side");
272
+    LOAD_CURSOR(XCURSOR_CURSOR_RIGHT_SIDE, "right_side");
273
+    LOAD_CURSOR(XCURSOR_CURSOR_BOTTOM_SIDE, "bottom_side");
274
+    LOAD_CURSOR(XCURSOR_CURSOR_LEFT_SIDE, "left_side");
275
 #undef LOAD_CURSOR
276
 }
277