]> git.sur5r.net Git - i3/i3/commitdiff
implement support for WM_TRANSIENT_FOR, expand testcase
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 13 Nov 2010 00:19:21 +0000 (01:19 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 13 Nov 2010 00:19:21 +0000 (01:19 +0100)
include/data.h
include/handlers.h
include/window.h
src/handlers.c
src/main.c
src/manage.c
src/window.c
testcases/t/14-client-leader.t

index 7acbb52ca29f376d81c3c232f0f5d37e3c247a9a..d34fd733af1ab472c8c1197ba5bbfcffba09f41b 100644 (file)
@@ -215,6 +215,7 @@ struct Window {
     /** Holds the xcb_window_t (just an ID) for the leader window (logical
      * parent for toolwindows and similar floating windows) */
     xcb_window_t leader;
+    xcb_window_t transient_for;
 
     char *class_class;
     char *class_instance;
index 80096e2ed79983890bc21fe0189eed8cb818c3e3..71e2bc92cf60e2b84c00662fd5634f715f8ac590 100644 (file)
@@ -179,7 +179,6 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state,
  */
 int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
                   xcb_atom_t name, xcb_get_property_reply_t *reply);
-#if 0
 
 /**
  * Handles the transient for hints set by a window, signalizing that this
@@ -191,7 +190,6 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
 int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state,
                          xcb_window_t window, xcb_atom_t name,
                          xcb_get_property_reply_t *reply);
-#endif
 
 /**
  * Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
index 071f4e2865ccd581b87aa26f2e85d5a0946beccd..6621a16969ce5776cdb67f69d3c4c64d84d07e8d 100644 (file)
@@ -30,4 +30,10 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop);
  */
 void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop);
 
+/**
+ * Updates the TRANSIENT_FOR (logical parent window).
+ *
+ */
+void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop);
+
 #endif
index d8b230f870b740b590eed145ccc92f106d69c91e..72cc68c536ce4349290446410065f4823189cc55 100644 (file)
@@ -811,8 +811,6 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
     return 1;
 }
 
-#if 0
-
 /*
  * Handles the transient for hints set by a window, signalizing that this window is a popup window
  * for some other window.
@@ -821,33 +819,34 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
  *
  */
 int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
-                         xcb_atom_t name, xcb_get_property_reply_t *reply) {
-        Client *client = table_get(&by_child, window);
-        if (client == NULL) {
-                DLOG("No such client\n");
-                return 1;
-        }
+                         xcb_atom_t name, xcb_get_property_reply_t *prop) {
+    Con *con;
 
-        xcb_window_t transient_for;
+    if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
+        DLOG("No such window\n");
+        return 1;
+    }
 
-        if (reply != NULL) {
-                if (!xcb_get_wm_transient_for_from_reply(&transient_for, reply))
-                        return 1;
-        } else {
-                if (!xcb_get_wm_transient_for_reply(conn, xcb_get_wm_transient_for_unchecked(conn, window),
-                                                    &transient_for, NULL))
-                        return 1;
-        }
+    if (prop == NULL) {
+        prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
+                                false, window, WM_TRANSIENT_FOR, WINDOW, 0, 32), NULL);
+        if (prop == NULL)
+            return 1;
+    }
 
-        if (client->floating == FLOATING_AUTO_OFF) {
-                DLOG("This is a popup window, putting into floating\n");
-                toggle_floating_mode(conn, client, true);
-        }
+    window_update_transient_for(con->window, prop);
 
-        return 1;
-}
+    // TODO: put window in floating mode if con->window->transient_for != XCB_NONE:
+#if 0
+    if (client->floating == FLOATING_AUTO_OFF) {
+        DLOG("This is a popup window, putting into floating\n");
+        toggle_floating_mode(conn, client, true);
+    }
 #endif
 
+    return 1;
+}
+
 /*
  * Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
  * toolwindow (or similar) and to which window it belongs (logical parent).
@@ -855,6 +854,10 @@ int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_
  */
 int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
                         xcb_atom_t name, xcb_get_property_reply_t *prop) {
+    Con *con;
+    if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+        return 1;
+
     if (prop == NULL) {
         prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
                                 false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL);
@@ -862,10 +865,6 @@ int handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state
             return 1;
     }
 
-    Con *con;
-    if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
-        return 1;
-
     window_update_leader(con->window, prop);
 
     return 1;
index 6ca3075ea2767e6a0d8267fe5115e1160ba8970e..787ff6fb0b1a1bd623ccf79c91fa009204a2134a 100644 (file)
@@ -270,6 +270,9 @@ int main(int argc, char *argv[]) {
     /* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */
     xcb_property_set_handler(&prophs, atoms[WM_CLIENT_LEADER], UINT_MAX, handle_clientleader_change, NULL);
 
+    /* Watch WM_TRANSIENT_FOR property (to which client this popup window belongs) */
+    xcb_property_set_handler(&prophs, WM_TRANSIENT_FOR, UINT_MAX, handle_transient_for, NULL);
+
     /* Set up the atoms we support */
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms);
     /* Set up the window manager’s name */
index 0fb2fc9bf20425073b09538b4a1e9a3da252309d..0307ea7a6501fcaac68b88bcf4e103cb37e19e68 100644 (file)
@@ -79,13 +79,14 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
     xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
                               utf8_title_cookie, title_cookie,
-                              class_cookie, leader_cookie;
+                              class_cookie, leader_cookie, transient_cookie;
 
     wm_type_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
     strut_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
     state_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_STATE], UINT32_MAX);
     utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[_NET_WM_NAME], 128);
     leader_cookie = xcb_get_any_property_unchecked(conn, false, window, atoms[WM_CLIENT_LEADER], UINT32_MAX);
+    transient_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_TRANSIENT_FOR, UINT32_MAX);
     title_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_NAME, 128);
     class_cookie = xcb_get_any_property_unchecked(conn, false, window, WM_CLASS, 128);
     /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */
@@ -145,6 +146,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL));
     window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL));
     window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
+    window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
 
     xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
     if (xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_DOCK])) {
@@ -185,12 +187,19 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
 
     /* set floating if necessary */
+    bool want_floating = false;
     if (xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_DIALOG]) ||
         xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_UTILITY]) ||
         xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_TOOLBAR]) ||
         xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_SPLASH])) {
         LOG("This window is a dialog window, setting floating\n");
+        want_floating = true;
+    }
+
+    if (cwindow->transient_for != XCB_NONE)
+        want_floating = true;
 
+    if (want_floating) {
         nc->rect.x = geom->x;
         nc->rect.y = geom->y;
         /* We respect the geometry wishes of floating windows, as long as they
index e22fb1c03bb1dbcc7d0ffa46b719243cea25157d..1cf167d84c0d8065785cd51e06dcd4221d6255d4 100644 (file)
@@ -115,3 +115,22 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
 
     win->leader = *leader;
 }
+
+/**
+ * Updates the TRANSIENT_FOR (logical parent window).
+ *
+ */
+void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
+    if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
+        DLOG("prop == NULL\n");
+        return;
+    }
+
+    xcb_window_t transient_for;
+    if (!xcb_get_wm_transient_for_from_reply(&transient_for, prop))
+        return;
+
+    DLOG("Transient for changed to %08x\n", transient_for);
+
+    win->transient_for = transient_for;
+}
index fb330d96084a3339147dff7c770186cf47b1015c..ef488f5a8e52edd986abe9a73a6c568588bfaef0 100644 (file)
@@ -1,7 +1,7 @@
 #!perl
 # vim:ts=4:sw=4:expandtab
 
-use i3test tests => 3;
+use i3test tests => 7;
 use X11::XCB qw(:all);
 use Time::HiRes qw(sleep);
 
@@ -15,6 +15,89 @@ my $i3 = i3("/tmp/nestedcons");
 my $tmp = get_unused_workspace();
 $i3->command("workspace $tmp")->recv;
 
+####################################################################################
+# first part: test if a floating window will be correctly positioned above its leader
+#
+# This is verified by opening two windows, then opening a floating window above the
+# right one, then above the left one. If the floating windows are all positioned alike,
+# one of both (depending on your screen resolution) will be positioned wrong.
+####################################################################################
+
+my $left = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [0, 0, 30, 30],
+    background_color => '#FF0000',
+);
+
+$left->name('Left');
+$left->map;
+
+my $right = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [0, 0, 30, 30],
+    background_color => '#FF0000',
+);
+
+$right->name('Right');
+$right->map;
+
+sleep 0.25;
+
+my ($abs, $rgeom) = $right->rect;
+
+my $child = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [ 0, 0, 30, 30 ],
+    background_color => '#C0C0C0',
+    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
+);
+
+$child->name('Child window');
+$child->client_leader($right);
+$child->map;
+
+sleep 0.25;
+
+my $cgeom;
+($abs, $cgeom) = $child->rect;
+cmp_ok($cgeom->x, '>=', $rgeom->x, 'Child X >= right container X');
+
+my $child2 = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [ 0, 0, 30, 30 ],
+    background_color => '#C0C0C0',
+    window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
+);
+
+$child2->name('Child window 2');
+$child2->client_leader($left);
+$child2->map;
+
+sleep 0.25;
+
+($abs, $cgeom) = $child2->rect;
+cmp_ok(($cgeom->x + $cgeom->width), '<', $rgeom->x, 'child above left window');
+
+# check wm_transient_for
+
+
+my $fwindow = $x->root->create_child(
+    class => WINDOW_CLASS_INPUT_OUTPUT,
+    rect => [ 0, 0, 30, 30],
+    background_color => '#FF0000',
+);
+
+$fwindow->transient_for($right);
+$fwindow->map;
+
+sleep 0.25;
+
+my ($absolute, $top) = $fwindow->rect;
+ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
+
+SKIP: {
+    skip "(workspace placement by client_leader not yet implemented)", 3;
+
 #####################################################################
 # Create a parent window
 #####################################################################
@@ -55,3 +138,5 @@ isnt($x->input_focus, $child->id, "Child window focused");
 $i3->command("workspace $tmp")->recv;
 
 is($x->input_focus, $child->id, "Child window focused");
+
+}