Implement the urgency hint for windows/workspaces
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).
This commit is contained in:
parent
e893902585
commit
2ff2a6a315
|
@ -380,10 +380,14 @@ client.focused_inactive::
|
||||||
the focus at the moment.
|
the focus at the moment.
|
||||||
client.unfocused::
|
client.unfocused::
|
||||||
A client which is not the focused one of its container.
|
A client which is not the focused one of its container.
|
||||||
|
client.urgent::
|
||||||
|
A client which has its urgency hint activated.
|
||||||
bar.focused::
|
bar.focused::
|
||||||
The current workspace in the bottom bar.
|
The current workspace in the bottom bar.
|
||||||
bar.unfocused::
|
bar.unfocused::
|
||||||
All other workspaces in the bottom bar.
|
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.
|
Colors are in HTML hex format, see below.
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,12 @@ struct Config {
|
||||||
struct Colortriple focused;
|
struct Colortriple focused;
|
||||||
struct Colortriple focused_inactive;
|
struct Colortriple focused_inactive;
|
||||||
struct Colortriple unfocused;
|
struct Colortriple unfocused;
|
||||||
|
struct Colortriple urgent;
|
||||||
} client;
|
} client;
|
||||||
struct config_bar {
|
struct config_bar {
|
||||||
struct Colortriple focused;
|
struct Colortriple focused;
|
||||||
struct Colortriple unfocused;
|
struct Colortriple unfocused;
|
||||||
|
struct Colortriple urgent;
|
||||||
} bar;
|
} bar;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,9 @@ struct Workspace {
|
||||||
/** Temporary flag needed for re-querying xinerama screens */
|
/** Temporary flag needed for re-querying xinerama screens */
|
||||||
bool reassigned;
|
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,
|
/** the client who is started in fullscreen mode on this workspace,
|
||||||
* NULL if there is none */
|
* NULL if there is none */
|
||||||
Client *fullscreen_client;
|
Client *fullscreen_client;
|
||||||
|
@ -400,6 +403,9 @@ struct Client {
|
||||||
* the screen and its requested size is used */
|
* the screen and its requested size is used */
|
||||||
bool dock;
|
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
|
/* After leaving fullscreen mode, a client needs to be reconfigured
|
||||||
* (configuration = setting X, Y, width and height). By setting the
|
* (configuration = setting X, Y, width and height). By setting the
|
||||||
* force_reconfigure flag, render_layout() will reconfigure the
|
* force_reconfigure flag, render_layout() will reconfigure the
|
||||||
|
|
|
@ -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_window_t window, xcb_atom_t name,
|
||||||
xcb_get_property_reply_t *reply);
|
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
|
* Handles the transient for hints set by a window, signalizing that this
|
||||||
* window is a popup window for some other window.
|
* window is a popup window for some other window.
|
||||||
|
|
|
@ -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);
|
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);
|
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
|
#endif
|
||||||
|
|
10
src/config.c
10
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.background = get_colorpixel(conn, "#222222");
|
||||||
config.client.unfocused.text = get_colorpixel(conn, "#888888");
|
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.border = get_colorpixel(conn, "#4c7899");
|
||||||
config.bar.focused.background = get_colorpixel(conn, "#285577");
|
config.bar.focused.background = get_colorpixel(conn, "#285577");
|
||||||
config.bar.focused.text = get_colorpixel(conn, "#ffffff");
|
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.background = get_colorpixel(conn, "#222222");
|
||||||
config.bar.unfocused.text = get_colorpixel(conn, "#888888");
|
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;
|
FILE *handle;
|
||||||
if (override_configpath != NULL) {
|
if (override_configpath != NULL) {
|
||||||
if ((handle = fopen(override_configpath, "r")) == 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", client.focused);
|
||||||
OPTION_COLORTRIPLE("client.focused_inactive", client.focused_inactive);
|
OPTION_COLORTRIPLE("client.focused_inactive", client.focused_inactive);
|
||||||
OPTION_COLORTRIPLE("client.unfocused", client.unfocused);
|
OPTION_COLORTRIPLE("client.unfocused", client.unfocused);
|
||||||
|
OPTION_COLORTRIPLE("client.urgent", client.urgent);
|
||||||
OPTION_COLORTRIPLE("bar.focused", bar.focused);
|
OPTION_COLORTRIPLE("bar.focused", bar.focused);
|
||||||
OPTION_COLORTRIPLE("bar.unfocused", bar.unfocused);
|
OPTION_COLORTRIPLE("bar.unfocused", bar.unfocused);
|
||||||
|
OPTION_COLORTRIPLE("bar.urgent", bar.urgent);
|
||||||
|
|
||||||
/* exec-lines (autostart) */
|
/* exec-lines (autostart) */
|
||||||
if (strcasecmp(key, "exec") == 0) {
|
if (strcasecmp(key, "exec") == 0) {
|
||||||
|
|
|
@ -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);
|
decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
|
||||||
else {
|
else {
|
||||||
uint32_t background_color;
|
uint32_t background_color;
|
||||||
|
if (client->urgent)
|
||||||
|
background_color = config.client.urgent.background;
|
||||||
/* Distinguish if the window is currently focused… */
|
/* 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;
|
background_color = config.client.focused.background;
|
||||||
/* …or if it is the focused window in a not focused container */
|
/* …or if it is the focused window in a not focused container */
|
||||||
else background_color = config.client.focused_inactive.background;
|
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;
|
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
|
* Handles the transient for hints set by a window, signalizing that this window is a popup window
|
||||||
* for some other window.
|
* for some other window.
|
||||||
|
|
36
src/layout.c
36
src/layout.c
|
@ -125,18 +125,23 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
|
||||||
return;
|
return;
|
||||||
|
|
||||||
last_focused = SLIST_FIRST(&(client->workspace->focus_stack));
|
last_focused = SLIST_FIRST(&(client->workspace->focus_stack));
|
||||||
if (client_is_floating(client)) {
|
/* Is the window urgent? */
|
||||||
if (last_focused == client)
|
if (client->urgent)
|
||||||
color = &(config.client.focused);
|
color = &(config.client.urgent);
|
||||||
else color = &(config.client.unfocused);
|
else {
|
||||||
} else {
|
if (client_is_floating(client)) {
|
||||||
if (client->container->currently_focused == client) {
|
if (last_focused == client)
|
||||||
/* Distinguish if the window is currently focused… */
|
|
||||||
if (last_focused == client && c_ws == client->workspace)
|
|
||||||
color = &(config.client.focused);
|
color = &(config.client.focused);
|
||||||
/* …or if it is the focused window in a not focused container */
|
else color = &(config.client.unfocused);
|
||||||
else color = &(config.client.focused_inactive);
|
} else {
|
||||||
} else color = &(config.client.unfocused);
|
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:
|
/* 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)
|
if (workspaces[c].screen != screen)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
struct Colortriple *color = (screen->current_workspace == c ? &(config.bar.focused) :
|
struct Colortriple *color;
|
||||||
&(config.bar.unfocused));
|
|
||||||
Workspace *ws = &workspaces[c];
|
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 */
|
/* Draw the outer rect */
|
||||||
xcb_draw_rect(conn, screen->bar, screen->bargc, color->border,
|
xcb_draw_rect(conn, screen->bar, screen->bargc, color->border,
|
||||||
drawn, /* x */
|
drawn, /* x */
|
||||||
|
|
|
@ -398,6 +398,9 @@ int main(int argc, char *argv[], char *env[]) {
|
||||||
/* Watch WM_CLIENT_LEADER (= logical parent window for toolbars etc.) */
|
/* 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);
|
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 */
|
/* Set up the atoms we support */
|
||||||
check_error(conn, xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED],
|
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");
|
ATOM, 32, 7, atoms), "Could not set _NET_SUPPORTED");
|
||||||
|
|
|
@ -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_CLASS);
|
||||||
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
|
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_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, 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[WM_CLIENT_LEADER]);
|
||||||
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
|
xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
|
||||||
|
|
|
@ -351,3 +351,21 @@ void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws) {
|
||||||
ignore_enter_notify_forall(conn, u_ws, false);
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue