diff --git a/docs/userguide b/docs/userguide index 215e4aa0..bda50245 100644 --- a/docs/userguide +++ b/docs/userguide @@ -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. diff --git a/include/config.h b/include/config.h index 17ec5391..100faa4c 100644 --- a/include/config.h +++ b/include/config.h @@ -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; }; diff --git a/include/data.h b/include/data.h index c942a96a..6d1869c0 100644 --- a/include/data.h +++ b/include/data.h @@ -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 diff --git a/include/handlers.h b/include/handlers.h index a56ed282..95194c14 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -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. diff --git a/include/workspace.h b/include/workspace.h index 31406a26..c77bc868 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -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 diff --git a/src/config.c b/src/config.c index d98f4a92..dd2f70c1 100644 --- a/src/config.c +++ b/src/config.c @@ -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) { diff --git a/src/handlers.c b/src/handlers.c index 49a56f7a..da051ef6 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -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. diff --git a/src/layout.c b/src/layout.c index f861171a..107fcd5a 100644 --- a/src/layout.c +++ b/src/layout.c @@ -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 */ diff --git a/src/mainx.c b/src/mainx.c index 69a6bbc2..db3d184a 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -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"); diff --git a/src/manage.c b/src/manage.c index 8005fc57..f4eecc96 100644 --- a/src/manage.c +++ b/src/manage.c @@ -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]); diff --git a/src/workspace.c b/src/workspace.c index e89591a2..cafc85eb 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -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; +}