i3 - improved tiling WM


Better detect windows that want floating

Patch status: needinfo

Patch by Kernc

Long description:

Windows matching certain criteria are floated by default. Currently,
only windows of type DIALOG, UTILITY, TOOLBAR, or SPLASH were floated.

This patch introduces more robust detection of what windows should be
floated. Besides aforementioned types, also NORMAL windows are floated
if they are either of fixed size, modal, or marked as skipping pager
or taskbar, as are all other (EWMH) window types except DESKTOP and
DOCK.

Closes ticket #1182.

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

b/include/atoms.xmacro

25
@@ -3,13 +3,16 @@ xmacro(_NET_SUPPORTING_WM_CHECK)
26
 xmacro(_NET_WM_NAME)
27
 xmacro(_NET_WM_STATE_FULLSCREEN)
28
 xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
29
+xmacro(_NET_WM_STATE_MODAL)
30
+xmacro(_NET_WM_STATE_SKIP_PAGER)
31
+xmacro(_NET_WM_STATE_SKIP_TASKBAR)
32
 xmacro(_NET_WM_STATE)
33
 xmacro(_NET_WM_WINDOW_TYPE)
34
 xmacro(_NET_WM_WINDOW_TYPE_DOCK)
35
 xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
36
 xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
37
-xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
38
-xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
39
+xmacro(_NET_WM_WINDOW_TYPE_DESKTOP)
40
+xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
41
 xmacro(_NET_WM_DESKTOP)
42
 xmacro(_NET_WM_STRUT_PARTIAL)
43
 xmacro(_NET_CLIENT_LIST_STACKING)

b/include/xcb_compat.h

48
@@ -19,9 +19,11 @@
49
 #define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL
50
 #define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN
51
 #define xcb_icccm_get_wm_size_hints_from_reply xcb_get_wm_size_hints_from_reply
52
+#define xcb_icccm_get_wm_size_hints_reply xcb_get_wm_size_hints_reply
53
 #define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply
54
 #define xcb_icccm_get_wm_normal_hints_unchecked xcb_get_wm_normal_hints_unchecked
55
 #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE
56
+#define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE XCB_SIZE_HINT_P_MAX_SIZE
57
 #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC
58
 #define XCB_ICCCM_SIZE_HINT_BASE_SIZE XCB_SIZE_HINT_BASE_SIZE
59
 #define XCB_ICCCM_SIZE_HINT_P_ASPECT XCB_SIZE_HINT_P_ASPECT

b/src/manage.c

64
@@ -105,6 +105,41 @@ static void ipc_send_window_new_event(Con *con) {
65
 }
66
 
67
 /*
68
+ * Hint whether window should be floating by default or not.
69
+ *
70
+ */
71
+static bool is_window_of_floating_type(xcb_window_t window,
72
+                                       xcb_get_property_reply_t *type_reply,
73
+                                       xcb_get_property_reply_t *state_reply) {
74
+    /* Never float desktop or docks. */
75
+    if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DOCK) ||
76
+        xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DESKTOP))
77
+        return false;
78
+
79
+    /* Only float normal windows if they are either of fixed size, modal,
80
+     * or marked as skip taskbar/pager. */
81
+    if (xcb_get_property_value_length(type_reply) == 0 ||  /* type not specified => NORMAL */
82
+        xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_NORMAL)) {
83
+        xcb_size_hints_t size_hints;
84
+        if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_MODAL) ||
85
+            xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_SKIP_PAGER) ||
86
+            xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_SKIP_TASKBAR) ||
87
+            (xcb_icccm_get_wm_size_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, window), &size_hints, NULL) &&
88
+             size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE &&
89
+             size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE &&
90
+             size_hints.min_width == size_hints.max_width &&
91
+             size_hints.min_height == size_hints.max_height))
92
+            return true;
93
+        return false;
94
+    }
95
+
96
+    /* Other windows are floating. Some are typically override_redirect
97
+     * windows (and thus not even managed), but not necessarily. See:
98
+     * http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#idp1333344 */
99
+    return true;
100
+}
101
+
102
+/*
103
  * Do some sanity checks and then reparent the window.
104
  *
105
  */
106
@@ -238,8 +273,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
107
     /* Where to start searching for a container that swallows the new one? */
108
     Con *search_at = croot;
109
 
110
-    xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
111
-    if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DOCK)) {
112
+    xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
113
+    if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DOCK)) {
114
         LOG("This window is of type dock\n");
115
         Output *output = get_output_containing(geom->x, geom->y);
116
         if (output != NULL) {
117
@@ -358,8 +393,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
118
         con_toggle_fullscreen(nc, CF_OUTPUT);
119
     }
120
 
121
-    FREE(state_reply);
122
-
123
     if (fs == NULL) {
124
         DLOG("Not in fullscreen mode, focusing\n");
125
         if (!cwindow->dock) {
126
@@ -392,15 +425,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
127
 
128
     /* set floating if necessary */
129
     bool want_floating = false;
130
-    if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DIALOG) ||
131
-        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_UTILITY) ||
132
-        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) ||
133
-        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_SPLASH)) {
134
-        LOG("This window is a dialog window, setting floating\n");
135
+    if (is_window_of_floating_type(window, type_reply, state_reply)) {
136
+        LOG("This window is of a floating window type, setting floating\n");
137
         want_floating = true;
138
     }
139
 
140
-    FREE(reply);
141
+    FREE(state_reply);
142
+    FREE(type_reply);
143
 
144
     if (cwindow->transient_for != XCB_NONE ||
145
         (cwindow->leader != XCB_NONE &&