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
|
||||
* parent for toolwindows and similar floating windows) */
|
||||
xcb_window_t leader;
|
||||
xcb_window_t transient_for;
|
||||
|
||||
char *class_class;
|
||||
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,
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,32 +819,33 @@ 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");
|
||||
xcb_atom_t name, xcb_get_property_reply_t *prop) {
|
||||
Con *con;
|
||||
|
||||
if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
|
||||
DLOG("No such window\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))
|
||||
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
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
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,
|
||||
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
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
# 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");
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue