Handle the EWMH atom _NET_WM_DESKTOP.

We already claim _NET_WM_DESKTOP support in _NET_SUPPORTED since around 2009,
but haven't actually done anything with it. However, especially pagers like
gnome-panel rely on this property to be updated and many tools, like GTK, want
to use the corresponding client messages to make a window sticky, move it
around etc.

This patch implements full support according to the EWMH spec. This means:

* We set the property on all windows when managing it.
* We keep the property updated on all windows at all times.
* We read and respect the property upon managing a window if it was set before
  mapping the window.
* We react to client messages for it.
* We remove the property on withdrawn windows.

Note that the special value 0xFFFFFFFF, according to the spec, means that the
window shall be shown on all workspaces. We do this by making it sticky and
float it. This shows it on all workspaces at least on the output it is on.

Furthermore, the spec gives us the freedom to ignore _NET_WM_DESKTOP when
managing a window if we have good reason to. In our case, we give window
swallowing a higher priority since the user would likely expect that and we
want to keep placeholder windows only around for as long as we have to.
However, we do prioritize this property over, for example, startup
notifications.

fixes #2153
fixes #1507
fixes #938
This commit is contained in:
Ingo Bürk
2016-01-11 20:53:26 +01:00
parent 9a4dbf366b
commit 328035fb7e
12 changed files with 609 additions and 48 deletions

View File

@ -503,6 +503,9 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
goto ignore_end;
}
/* Since we close the container, we need to unset _NET_WM_DESKTOP according to the spec. */
xcb_delete_property(conn, event->window, A__NET_WM_DESKTOP);
tree_close_internal(con, DONT_KILL_WINDOW, false, false);
tree_render();
@ -735,7 +738,9 @@ static void handle_client_message(xcb_client_message_event_t *event) {
con->sticky = !con->sticky;
DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
ewmh_update_sticky(con->window->id, con->sticky);
output_push_sticky_windows(focused);
ewmh_update_wm_desktop();
}
tree_render();
@ -839,32 +844,48 @@ static void handle_client_message(xcb_client_message_event_t *event) {
* a request to focus the given workspace. See
* http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
* */
Con *output;
uint32_t idx = 0;
DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
if (idx == event->data.data32[0]) {
/* data32[1] is a timestamp used to prevent focus race conditions */
if (event->data.data32[1])
last_timestamp = event->data.data32[1];
DLOG("Handling request to focus workspace %s\n", ws->name);
workspace_show(ws);
tree_render();
return;
}
++idx;
}
Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]);
if (ws == NULL) {
ELOG("Could not determine workspace for this index, ignoring request.\n");
return;
}
DLOG("Handling request to focus workspace %s\n", ws->name);
workspace_show(ws);
tree_render();
} else if (event->type == A__NET_WM_DESKTOP) {
uint32_t index = event->data.data32[0];
DLOG("Request to move window %d to EWMH desktop index %d\n", event->window, index);
Con *con = con_by_window_id(event->window);
if (con == NULL) {
DLOG("Couldn't find con for window %d, ignoring the request.\n", event->window);
return;
}
if (index == NET_WM_DESKTOP_ALL) {
/* The window is requesting to be visible on all workspaces, so
* let's float it and make it sticky. */
DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n");
floating_enable(con, false);
con->sticky = true;
ewmh_update_sticky(con->window->id, true);
output_push_sticky_windows(focused);
} else {
Con *ws = ewmh_get_workspace_by_index(index);
if (ws == NULL) {
ELOG("Could not determine workspace for this index, ignoring request.\n");
return;
}
con_move_to_workspace(con, ws, false, false, true);
}
tree_render();
ewmh_update_wm_desktop();
} else if (event->type == A__NET_CLOSE_WINDOW) {
/*
* Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
@ -915,8 +936,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
break;
}
} else {
DLOG("unhandled clientmessage\n");
return;
DLOG("Skipping client message for unhandled type %d\n", event->type);
}
}