]> git.sur5r.net Git - i3/i3/commitdiff
Implement the urgency hint for windows/workspaces
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 6 Sep 2009 20:40:11 +0000 (22:40 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 6 Sep 2009 20:40:11 +0000 (22:40 +0200)
Thanks to Mikael for bringing it to my mind. This change introduces
two new color classes, client.urgent and bar.urgent. By default,
urgent clients are drawn in red (colors by Atsutane).

docs/userguide
include/config.h
include/data.h
include/handlers.h
include/workspace.h
src/config.c
src/handlers.c
src/layout.c
src/mainx.c
src/manage.c
src/workspace.c

index 215e4aa02fadd7c853a7f3d8986d0d15e5c4ffc8..bda502457c3635af26142291398f6087c85a11d7 100644 (file)
@@ -380,10 +380,14 @@ client.focused_inactive::
        the focus at the moment.
 client.unfocused::
        A client which is not the focused one of its container.
+client.urgent::
+       A client which has its urgency hint activated.
 bar.focused::
        The current workspace in the bottom bar.
 bar.unfocused::
        All other workspaces in the bottom bar.
+bar.urgent::
+       A workspace which has at least one client with an activated urgency hint.
 
 Colors are in HTML hex format, see below.
 
index 17ec5391c1aa2b676e44c6beeb8a33f214852745..100faa4c164b98b47e79d91105697c5a8eba1945 100644 (file)
@@ -64,10 +64,12 @@ struct Config {
                 struct Colortriple focused;
                 struct Colortriple focused_inactive;
                 struct Colortriple unfocused;
+                struct Colortriple urgent;
         } client;
         struct config_bar {
                 struct Colortriple focused;
                 struct Colortriple unfocused;
+                struct Colortriple urgent;
         } bar;
 };
 
index c942a96ac8a9b79a09089bc949eb52406a7d4e6f..6d1869c0bfe8f79400a927c5d47276bf497bf6e0 100644 (file)
@@ -201,6 +201,9 @@ struct Workspace {
         /** Temporary flag needed for re-querying xinerama screens */
         bool reassigned;
 
+        /** True if any client on this workspace has its urgent flag set */
+        bool urgent;
+
         /** the client who is started in fullscreen mode on this workspace,
          * NULL if there is none */
         Client *fullscreen_client;
@@ -400,6 +403,9 @@ struct Client {
          * the screen and its requested size is used */
         bool dock;
 
+        /** True if the client set the urgency flag in its WM_HINTS property */
+        bool urgent;
+
         /* After leaving fullscreen mode, a client needs to be reconfigured
          * (configuration = setting X, Y, width and height). By setting the
          * force_reconfigure flag, render_layout() will reconfigure the
index a56ed2825e45e797c5f597ecf4beb4fc56d34a82..95194c145c294d4a2854977d6b852437ee8393cb 100644 (file)
@@ -160,6 +160,13 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state,
                         xcb_window_t window, xcb_atom_t name,
                         xcb_get_property_reply_t *reply);
 
+/**
+ * Handles the WM_HINTS property for extracting the urgency state of the 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);
+
 /**
  * Handles the transient for hints set by a window, signalizing that this
  * window is a popup window for some other window.
index 31406a26be644dab99f9b7d32871c0ed8aa216a6..c77bc868faa30f5ce969a1b06c3b4d36c3f5c898 100644 (file)
@@ -62,7 +62,17 @@ Workspace *get_first_workspace_for_screen(struct screens_head *slist, i3Screen *
  */
 void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws);
 
-
+/**
+ * Maps all clients (and stack windows) of the given workspace.
+ *
+ */
 void workspace_map_clients(xcb_connection_t *conn, Workspace *ws);
 
+/**
+ * Goes through all clients on the given workspace and updates the workspace’s
+ * urgent flag accordingly.
+ *
+ */
+void workspace_update_urgent_flag(Workspace *ws);
+
 #endif
index d98f4a92e4e6e357b28eb02e678eb531316d0075..dd2f70c1ef7057cb31c9c4f304c1fd3da2020262 100644 (file)
@@ -210,6 +210,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
         config.client.unfocused.background = get_colorpixel(conn, "#222222");
         config.client.unfocused.text = get_colorpixel(conn, "#888888");
 
+        config.client.urgent.border = get_colorpixel(conn, "#2f343a");
+        config.client.urgent.background = get_colorpixel(conn, "#900000");
+        config.client.urgent.text = get_colorpixel(conn, "#ffffff");
+
         config.bar.focused.border = get_colorpixel(conn, "#4c7899");
         config.bar.focused.background = get_colorpixel(conn, "#285577");
         config.bar.focused.text = get_colorpixel(conn, "#ffffff");
@@ -218,6 +222,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
         config.bar.unfocused.background = get_colorpixel(conn, "#222222");
         config.bar.unfocused.text = get_colorpixel(conn, "#888888");
 
+        config.bar.urgent.border = get_colorpixel(conn, "#2f343a");
+        config.bar.urgent.background = get_colorpixel(conn, "#900000");
+        config.bar.urgent.text = get_colorpixel(conn, "#ffffff");
+
         FILE *handle;
         if (override_configpath != NULL) {
                 if ((handle = fopen(override_configpath, "r")) == NULL)
@@ -260,8 +268,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
                 OPTION_COLORTRIPLE("client.focused", client.focused);
                 OPTION_COLORTRIPLE("client.focused_inactive", client.focused_inactive);
                 OPTION_COLORTRIPLE("client.unfocused", client.unfocused);
+                OPTION_COLORTRIPLE("client.urgent", client.urgent);
                 OPTION_COLORTRIPLE("bar.focused", bar.focused);
                 OPTION_COLORTRIPLE("bar.unfocused", bar.unfocused);
+                OPTION_COLORTRIPLE("bar.urgent", bar.urgent);
 
                 /* exec-lines (autostart) */
                 if (strcasecmp(key, "exec") == 0) {
index 49a56f7adbcd055d6ea273c5276b832ae8a41fbb..da051ef6b310288ef2feff99da40cacf29554173 100644 (file)
@@ -933,8 +933,10 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
                 decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
         else {
                 uint32_t background_color;
+                if (client->urgent)
+                        background_color = config.client.urgent.background;
                 /* Distinguish if the window is currently focused… */
-                if (CUR_CELL->currently_focused == client)
+                else if (CUR_CELL->currently_focused == client)
                         background_color = config.client.focused.background;
                 /* …or if it is the focused window in a not focused container */
                 else background_color = config.client.focused_inactive.background;
@@ -1100,6 +1102,43 @@ int handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_w
         return 1;
 }
 
+/*
+ * Handles the WM_HINTS property for extracting the urgency state of the 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) {
+        Client *client = table_get(&by_child, window);
+        if (client == NULL) {
+                LOG("Received WM_HINTS for unknown client\n");
+                return 1;
+        }
+        xcb_wm_hints_t hints;
+
+        if (reply != NULL)
+                xcb_get_wm_hints_from_reply(&hints, reply);
+        else
+                xcb_get_wm_hints_reply(conn, xcb_get_wm_hints_unchecked(conn, client->child), &hints, NULL);
+
+        /* Update the flag on the client directly */
+        client->urgent = (xcb_wm_hints_get_urgency(&hints) != 0);
+        CLIENT_LOG(client);
+        LOG("Urgency flag changed to %d\n", client->urgent);
+
+        workspace_update_urgent_flag(client->workspace);
+        redecorate_window(conn, client);
+
+        /* If the workspace this client is on is not visible, we need to redraw
+         * the workspace bar */
+        if (!workspace_is_visible(client->workspace)) {
+                i3Screen *screen = client->workspace->screen;
+                render_workspace(conn, screen, &(workspaces[screen->current_workspace]));
+                xcb_flush(conn);
+        }
+
+        return 1;
+}
+
 /*
  * Handles the transient for hints set by a window, signalizing that this window is a popup window
  * for some other window.
index f861171ac0db4dce4774c5f7b50bed2edaaab159..107fcd5a55c19169d3c3cbf7f4a456e5cf74c6b3 100644 (file)
@@ -125,18 +125,23 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
                 return;
 
         last_focused = SLIST_FIRST(&(client->workspace->focus_stack));
-        if (client_is_floating(client)) {
-                if (last_focused == client)
-                        color = &(config.client.focused);
-                else color = &(config.client.unfocused);
-        } else {
-                if (client->container->currently_focused == client) {
-                        /* Distinguish if the window is currently focused… */
-                        if (last_focused == client && c_ws == client->workspace)
+        /* Is the window urgent? */
+        if (client->urgent)
+                color = &(config.client.urgent);
+        else {
+                if (client_is_floating(client)) {
+                        if (last_focused == client)
                                 color = &(config.client.focused);
-                        /* …or if it is the focused window in a not focused container */
-                        else color = &(config.client.focused_inactive);
-                } else color = &(config.client.unfocused);
+                        else color = &(config.client.unfocused);
+                } else {
+                        if (client->container->currently_focused == client) {
+                                /* Distinguish if the window is currently focused… */
+                                if (last_focused == client && c_ws == client->workspace)
+                                        color = &(config.client.focused);
+                                /* …or if it is the focused window in a not focused container */
+                                else color = &(config.client.focused_inactive);
+                        } else color = &(config.client.unfocused);
+                }
         }
 
         /* Our plan is the following:
@@ -515,10 +520,15 @@ static void render_internal_bar(xcb_connection_t *conn, Workspace *r_ws, int wid
                 if (workspaces[c].screen != screen)
                         continue;
 
-                struct Colortriple *color = (screen->current_workspace == c ? &(config.bar.focused) :
-                                             &(config.bar.unfocused));
+                struct Colortriple *color;
                 Workspace *ws = &workspaces[c];
 
+                if (screen->current_workspace == c)
+                        color = &(config.bar.focused);
+                else if (ws->urgent)
+                        color = &(config.bar.urgent);
+                else color = &(config.bar.unfocused);
+
                 /* Draw the outer rect */
                 xcb_draw_rect(conn, screen->bar, screen->bargc, color->border,
                               drawn,              /* x */
index 69a6bbc235b2f8aea1ac397d2984a5a62bf5daa0..db3d184aa1a88d6e10d2d28af5fb7e291a0f9408 100644 (file)
@@ -398,6 +398,9 @@ int main(int argc, char *argv[], char *env[]) {
         /* 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_HINTS (contains the urgent property) */
+        xcb_property_set_handler(&prophs, WM_HINTS, UINT_MAX, handle_hints, NULL);
+
         /* Set up the atoms we support */
         check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
                        ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
index 8005fc57dcd0b597dbae55208ad917641824d50a..f4eecc966be9353dfa325537acb14620a14269fd 100644 (file)
@@ -105,6 +105,7 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
+        xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
index e89591a26d670b6722465c53e1f0a368775b58c7..cafc85ebc9e25e786c90344d73cd81aca29d60a3 100644 (file)
@@ -351,3 +351,21 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) {
         ignore_enter_notify_forall(conn, u_ws, false);
 }
 
+/*
+ * Goes through all clients on the given workspace and updates the workspace’s
+ * urgent flag accordingly.
+ *
+ */
+void workspace_update_urgent_flag(Workspace *ws) {
+        Client *current;
+
+        SLIST_FOREACH(current, &(ws->focus_stack), focus_clients) {
+                if (!current->urgent)
+                        continue;
+
+                ws->urgent = true;
+                return;
+        }
+
+        ws->urgent = false;
+}