We will respect this atom upon managing a window as well as when we receive a request that changes the sticky state.
fixes #1455
xmacro(_NET_WM_NAME)
xmacro(_NET_WM_VISIBLE_NAME)
xmacro(_NET_WM_MOVERESIZE)
+xmacro(_NET_WM_STATE_STICKY)
xmacro(_NET_WM_STATE_FULLSCREEN)
xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
xmacro(_NET_WM_STATE_MODAL)
*/
bool con_is_hidden(Con *con);
+/**
+ * Returns whether the container or any of its children is sticky.
+ *
+ */
+bool con_is_sticky(Con *con);
+
/**
* Returns true if this node has regular or floating children.
*
TAILQ_HEAD(swallow_head, Match) swallow_head;
fullscreen_mode_t fullscreen_mode;
+
+ /* Whether this window should stick to the glass. This corresponds to
+ * the _NET_WM_STATE_STICKY atom and will only be respected if the
+ * window is floating. */
+ bool sticky;
+
/* layout is the layout of this container: one of split[v|h], stacked or
* tabbed. Special containers in the tree (above workspaces) have special
* layouts like dockarea or output.
return false;
}
+/*
+ * Returns whether the container or any of its children is sticky.
+ *
+ */
+bool con_is_sticky(Con *con) {
+ if (con->sticky)
+ return true;
+
+ Con *child;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
+ if (con_is_sticky(child))
+ return true;
+ }
+
+ return false;
+}
+
/*
* Returns true if this node accepts a window (if the node swallows windows,
* it might already have swallowed enough and cannot hold any more).
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
/* only send the first 31 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
- xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ 31, supported_atoms);
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ 32, supported_atoms);
/* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */
xcb_map_window(conn, ewmh_window);
if (event->type == A__NET_WM_STATE) {
if (event->format != 32 ||
(event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN &&
- event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION)) {
+ event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION &&
+ event->data.data32[1] != A__NET_WM_STATE_STICKY)) {
DLOG("Unknown atom in clientmessage of type %d\n", event->data.data32[1]);
return;
}
con_set_urgency(con, false);
else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE)
con_set_urgency(con, !con->urgent);
+ } else if (event->data.data32[1] == A__NET_WM_STATE_STICKY) {
+ DLOG("Received a client message to modify _NET_WM_STATE_STICKY.\n");
+ if (event->data.data32[0] == _NET_WM_STATE_ADD)
+ con->sticky = true;
+ else if (event->data.data32[0] == _NET_WM_STATE_REMOVE)
+ con->sticky = false;
+ else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE)
+ con->sticky = !con->sticky;
+
+ DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
}
tree_render();
ystr("fullscreen_mode");
y(integer, con->fullscreen_mode);
+ ystr("sticky");
+ y(bool, con->sticky);
+
ystr("floating");
switch (con->floating) {
case FLOATING_AUTO_OFF:
to_focus = json_node;
}
+ if (strcasecmp(last_key, "sticky") == 0)
+ json_node->sticky = val;
+
if (parsing_swallows) {
if (strcasecmp(last_key, "restart_mode") == 0)
current_swallow->restart_mode = val;
want_floating = true;
}
+ if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY))
+ nc->sticky = true;
+
FREE(state_reply);
FREE(type_reply);
/* Update the EWMH hints */
ewmh_update_current_desktop();
+
+ /* Floating containers which are sticky need to be moved to the new workspace,
+ * but only on the same output. */
+ if (current != NULL && old_output == new_output) {
+ Con *prev = focused;
+ Con *child;
+
+ /* We can't simply iterate over the floating containers since moving a
+ * sticky container to the target workspace will modify that list.
+ * Instead, we first count the number of sticky containers, then memorize
+ * all of them and finally loop over that list to move them to the new
+ * workspace. */
+ int num_sticky = 0;
+ TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
+ if (con_is_sticky(child))
+ num_sticky++;
+ }
+
+ Con *sticky_cons[num_sticky];
+ int ctr = 0;
+ TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
+ if (con_is_sticky(child))
+ sticky_cons[ctr++] = child;
+ }
+
+ for (int i = 0; i < num_sticky; i++) {
+ con_move_to_workspace(sticky_cons[i], workspace, true, false);
+
+ /* We want sticky containers to be at the end of the focus head. */
+ TAILQ_REMOVE(&(workspace->focus_head), sticky_cons[i], focused);
+ TAILQ_INSERT_TAIL(&(workspace->focus_head), sticky_cons[i], focused);
+ }
+
+ /* Focus the correct container since moving the sticky containers
+ * changed the focus. However, if no container was focused before,
+ * we can leave the focus at the sticky container. */
+ if (prev != croot)
+ con_focus(prev);
+ }
}
/*
my $expected = {
fullscreen_mode => 0,
+ sticky => $ignore,
nodes => $ignore,
window => undef,
name => 'root',