implement support for WM_TRANSIENT_FOR, expand testcase
This commit is contained in:
parent
ad9be5402a
commit
432073dbe5
|
@ -215,6 +215,7 @@ struct Window {
|
||||||
/** Holds the xcb_window_t (just an ID) for the leader window (logical
|
/** Holds the xcb_window_t (just an ID) for the leader window (logical
|
||||||
* parent for toolwindows and similar floating windows) */
|
* parent for toolwindows and similar floating windows) */
|
||||||
xcb_window_t leader;
|
xcb_window_t leader;
|
||||||
|
xcb_window_t transient_for;
|
||||||
|
|
||||||
char *class_class;
|
char *class_class;
|
||||||
char *class_instance;
|
char *class_instance;
|
||||||
|
|
|
@ -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,
|
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);
|
xcb_atom_t name, xcb_get_property_reply_t *reply);
|
||||||
#if 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the transient for hints set by a window, signalizing that this
|
* 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,
|
int handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state,
|
||||||
xcb_window_t window, xcb_atom_t name,
|
xcb_window_t window, xcb_atom_t name,
|
||||||
xcb_get_property_reply_t *reply);
|
xcb_get_property_reply_t *reply);
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
|
* Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
|
||||||
|
|
|
@ -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);
|
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
|
#endif
|
||||||
|
|
|
@ -811,8 +811,6 @@ int handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles the transient for hints set by a window, signalizing that this window is a popup window
|
* Handles the transient for hints set by a window, signalizing that this window is a popup window
|
||||||
* for some other 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,
|
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) {
|
xcb_atom_t name, xcb_get_property_reply_t *prop) {
|
||||||
Client *client = table_get(&by_child, window);
|
Con *con;
|
||||||
if (client == NULL) {
|
|
||||||
DLOG("No such client\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_window_t transient_for;
|
|
||||||
|
|
||||||
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 (client->floating == FLOATING_AUTO_OFF) {
|
|
||||||
DLOG("This is a popup window, putting into floating\n");
|
|
||||||
toggle_floating_mode(conn, client, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
|
||||||
|
DLOG("No such window\n");
|
||||||
return 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_update_transient_for(con->window, prop);
|
||||||
|
|
||||||
|
// 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
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
|
* 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).
|
* 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,
|
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) {
|
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) {
|
if (prop == NULL) {
|
||||||
prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
|
prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
|
||||||
false, window, WM_CLIENT_LEADER, WINDOW, 0, 32), NULL);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Con *con;
|
|
||||||
if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
window_update_leader(con->window, prop);
|
window_update_leader(con->window, prop);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -270,6 +270,9 @@ int main(int argc, char *argv[]) {
|
||||||
/* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */
|
/* 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);
|
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 */
|
/* Set up the atoms we support */
|
||||||
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms);
|
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 7, atoms);
|
||||||
/* Set up the window manager’s name */
|
/* Set up the window manager’s name */
|
||||||
|
|
11
src/manage.c
11
src/manage.c
|
@ -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,
|
xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
|
||||||
utf8_title_cookie, title_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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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 */
|
/* 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_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_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_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);
|
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])) {
|
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 */
|
/* set floating if necessary */
|
||||||
|
bool want_floating = false;
|
||||||
if (xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_DIALOG]) ||
|
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_UTILITY]) ||
|
||||||
xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_TOOLBAR]) ||
|
xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_TOOLBAR]) ||
|
||||||
xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_SPLASH])) {
|
xcb_reply_contains_atom(reply, atoms[_NET_WM_WINDOW_TYPE_SPLASH])) {
|
||||||
LOG("This window is a dialog window, setting floating\n");
|
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.x = geom->x;
|
||||||
nc->rect.y = geom->y;
|
nc->rect.y = geom->y;
|
||||||
/* We respect the geometry wishes of floating windows, as long as they
|
/* We respect the geometry wishes of floating windows, as long as they
|
||||||
|
|
19
src/window.c
19
src/window.c
|
@ -115,3 +115,22 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
|
||||||
|
|
||||||
win->leader = *leader;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!perl
|
#!perl
|
||||||
# vim:ts=4:sw=4:expandtab
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
|
||||||
use i3test tests => 3;
|
use i3test tests => 7;
|
||||||
use X11::XCB qw(:all);
|
use X11::XCB qw(:all);
|
||||||
use Time::HiRes qw(sleep);
|
use Time::HiRes qw(sleep);
|
||||||
|
|
||||||
|
@ -15,6 +15,89 @@ my $i3 = i3("/tmp/nestedcons");
|
||||||
my $tmp = get_unused_workspace();
|
my $tmp = get_unused_workspace();
|
||||||
$i3->command("workspace $tmp")->recv;
|
$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
|
# Create a parent window
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
@ -55,3 +138,5 @@ isnt($x->input_focus, $child->id, "Child window focused");
|
||||||
$i3->command("workspace $tmp")->recv;
|
$i3->command("workspace $tmp")->recv;
|
||||||
|
|
||||||
is($x->input_focus, $child->id, "Child window focused");
|
is($x->input_focus, $child->id, "Child window focused");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue