Merge pull request #2276 from RyanDwyer/urgency

Implement urgency base functionality
This commit is contained in:
Drew DeVault 2018-07-16 15:39:08 -07:00 committed by GitHub
commit d6bd314dff
Failed to generate hash of commit
18 changed files with 250 additions and 18 deletions

View file

@ -152,6 +152,7 @@ sway_cmd cmd_swaybg_command;
sway_cmd cmd_swap; sway_cmd cmd_swap;
sway_cmd cmd_title_format; sway_cmd cmd_title_format;
sway_cmd cmd_unmark; sway_cmd cmd_unmark;
sway_cmd cmd_urgent;
sway_cmd cmd_workspace; sway_cmd cmd_workspace;
sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_ws_auto_back_and_forth;
sway_cmd cmd_workspace_layout; sway_cmd cmd_workspace_layout;

View file

@ -316,4 +316,6 @@ void container_floating_move_to(struct sway_container *con,
*/ */
void container_set_dirty(struct sway_container *container); void container_set_dirty(struct sway_container *container);
bool container_has_urgent_child(struct sway_container *container);
#endif #endif

View file

@ -70,6 +70,10 @@ struct sway_view {
bool border_left; bool border_left;
bool border_right; bool border_right;
struct timespec urgent;
bool allow_request_urgent;
struct wl_event_source *urgent_timer;
bool destroying; bool destroying;
list_t *executed_criteria; // struct criteria * list_t *executed_criteria; // struct criteria *
@ -305,4 +309,8 @@ void view_update_marks_textures(struct sway_view *view);
*/ */
bool view_is_visible(struct sway_view *view); bool view_is_visible(struct sway_view *view);
void view_set_urgent(struct sway_view *view, bool enable);
bool view_is_urgent(struct sway_view *view);
#endif #endif

View file

@ -10,6 +10,7 @@ struct sway_workspace {
struct sway_view *fullscreen; struct sway_view *fullscreen;
struct sway_container *floating; struct sway_container *floating;
list_t *output_priority; list_t *output_priority;
bool urgent;
}; };
extern char *prev_workspace_name; extern char *prev_workspace_name;
@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace,
struct sway_container *workspace_output_get_highest_available( struct sway_container *workspace_output_get_highest_available(
struct sway_container *ws, struct sway_container *exclude); struct sway_container *ws, struct sway_container *exclude);
void workspace_detect_urgent(struct sway_container *workspace);
#endif #endif

View file

@ -154,6 +154,7 @@ static struct cmd_handler command_handlers[] = {
{ "swap", cmd_swap }, { "swap", cmd_swap },
{ "title_format", cmd_title_format }, { "title_format", cmd_title_format },
{ "unmark", cmd_unmark }, { "unmark", cmd_unmark },
{ "urgent", cmd_urgent },
}; };
static int handler_compare(const void *_a, const void *_b) { static int handler_compare(const void *_a, const void *_b) {

36
sway/commands/urgent.c Normal file
View file

@ -0,0 +1,36 @@
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/layout.h"
struct cmd_results *cmd_urgent(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
return error;
}
struct sway_container *container =
config->handler_context.current_container;
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "urgent",
"Only views can be urgent");
}
struct sway_view *view = container->sway_view;
if (strcmp(argv[0], "enable") == 0) {
view_set_urgent(view, true);
} else if (strcmp(argv[0], "disable") == 0) {
view_set_urgent(view, false);
} else if (strcmp(argv[0], "allow") == 0) {
view->allow_request_urgent = true;
} else if (strcmp(argv[0], "deny") == 0) {
view->allow_request_urgent = false;
} else {
return cmd_results_new(CMD_INVALID, "urgent",
"Expected 'urgent <enable|disable|allow|deny>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) {
return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
} }
static int cmp_urgent(const void *_a, const void *_b) {
struct sway_view *a = *(void **)_a;
struct sway_view *b = *(void **)_b;
if (a->urgent.tv_sec < b->urgent.tv_sec) {
return -1;
} else if (a->urgent.tv_sec > b->urgent.tv_sec) {
return 1;
}
if (a->urgent.tv_nsec < b->urgent.tv_nsec) {
return -1;
} else if (a->urgent.tv_nsec > b->urgent.tv_nsec) {
return 1;
}
return 0;
}
static void find_urgent_iterator(struct sway_container *swayc, void *data) {
if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) {
return;
}
list_t *urgent_views = data;
list_add(urgent_views, swayc->sway_view);
}
static bool criteria_matches_view(struct criteria *criteria, static bool criteria_matches_view(struct criteria *criteria,
struct sway_view *view) { struct sway_view *view) {
if (criteria->title) { if (criteria->title) {
@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria,
} }
if (criteria->urgent) { if (criteria->urgent) {
// TODO if (!view_is_urgent(view)) {
return false; return false;
}
list_t *urgent_views = create_list();
container_for_each_descendant_dfs(&root_container,
find_urgent_iterator, urgent_views);
list_stable_sort(urgent_views, cmp_urgent);
struct sway_view *target;
if (criteria->urgent == 'o') { // oldest
target = urgent_views->items[0];
} else { // latest
target = urgent_views->items[urgent_views->length - 1];
}
list_free(urgent_views);
if (view != target) {
return false;
}
} }
if (criteria->workspace) { if (criteria->workspace) {

View file

@ -553,7 +553,11 @@ static void render_container_simple(struct sway_output *output,
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
struct sway_container_state *state = &child->current; struct sway_container_state *state = &child->current;
if (state->focused || parent_focused) { if (view_is_urgent(view)) {
colors = &config->border_colors.urgent;
title_texture = child->title_urgent;
marks_texture = view->marks_urgent;
} else if (state->focused || parent_focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view->marks_focused; marks_texture = view->marks_focused;
@ -607,8 +611,14 @@ static void render_container_tabbed(struct sway_output *output,
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
bool urgent = view ?
view_is_urgent(view) : container_has_urgent_child(child);
if (cstate->focused || parent_focused) { if (urgent) {
colors = &config->border_colors.urgent;
title_texture = child->title_urgent;
marks_texture = view ? view->marks_urgent : NULL;
} else if (cstate->focused || parent_focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view ? view->marks_focused : NULL; marks_texture = view ? view->marks_focused : NULL;
@ -670,8 +680,14 @@ static void render_container_stacked(struct sway_output *output,
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
bool urgent = view ?
view_is_urgent(view) : container_has_urgent_child(child);
if (cstate->focused || parent_focused) { if (urgent) {
colors = &config->border_colors.urgent;
title_texture = child->title_urgent;
marks_texture = view ? view->marks_urgent : NULL;
} else if (cstate->focused || parent_focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view ? view->marks_focused : NULL; marks_texture = view ? view->marks_focused : NULL;
@ -731,7 +747,11 @@ static void render_floating_container(struct sway_output *soutput,
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
if (con->current.focused) { if (view_is_urgent(view)) {
colors = &config->border_colors.urgent;
title_texture = con->title_urgent;
marks_texture = view->marks_urgent;
} else if (con->current.focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = con->title_focused; title_texture = con->title_focused;
marks_texture = view->marks_focused; marks_texture = view->marks_focused;

View file

@ -297,6 +297,10 @@ static void handle_commit(struct wl_listener *listener, void *data) {
} }
view_damage_from(view); view_damage_from(view);
if (view->allow_request_urgent) {
view_set_urgent(view, (bool)xsurface->hints_urgency);
}
} }
static void handle_unmap(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) {

View file

@ -594,6 +594,12 @@ static void seat_send_unfocus(struct sway_container *container,
} }
} }
static int handle_urgent_timeout(void *data) {
struct sway_view *view = data;
view_set_urgent(view, false);
return 0;
}
void seat_set_focus_warp(struct sway_seat *seat, void seat_set_focus_warp(struct sway_seat *seat,
struct sway_container *container, bool warp) { struct sway_container *container, bool warp) {
if (seat->focused_layer) { if (seat->focused_layer) {
@ -671,6 +677,16 @@ void seat_set_focus_warp(struct sway_seat *seat,
} }
} }
// If urgent, start a timer to unset it
if (container && container->type == C_VIEW &&
view_is_urgent(container->sway_view) &&
!container->sway_view->urgent_timer) {
struct sway_view *view = container->sway_view;
view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,
handle_urgent_timeout, view);
wl_event_source_timer_update(view->urgent_timer, 1000);
}
// If we've focused a floating container, bring it to the front. // If we've focused a floating container, bring it to the front.
// We do this by putting it at the end of the floating list. // We do this by putting it at the end of the floating list.
// This must happen for both the pending and current children lists. // This must happen for both the pending and current children lists.

View file

@ -170,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace,
json_object_object_add(object, "output", workspace->parent ? json_object_object_add(object, "output", workspace->parent ?
json_object_new_string(workspace->parent->name) : NULL); json_object_new_string(workspace->parent->name) : NULL);
json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "type", json_object_new_string("workspace"));
json_object_object_add(object, "urgent", json_object_new_boolean(false)); json_object_object_add(object, "urgent",
json_object_new_boolean(workspace->sway_workspace->urgent));
json_object_object_add(object, "representation", workspace->formatted_title ? json_object_object_add(object, "representation", workspace->formatted_title ?
json_object_new_string(workspace->formatted_title) : NULL); json_object_new_string(workspace->formatted_title) : NULL);
@ -196,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
json_object_object_add(object, "layout", json_object_object_add(object, "layout",
json_object_new_string(ipc_json_layout_description(c->layout))); json_object_new_string(ipc_json_layout_description(c->layout)));
} }
bool urgent = c->type == C_VIEW ?
view_is_urgent(c->sway_view) : container_has_urgent_child(c);
json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
} }
static void focus_inactive_children_iterator(struct sway_container *c, void *data) { static void focus_inactive_children_iterator(struct sway_container *c, void *data) {

View file

@ -77,6 +77,7 @@ sway_sources = files(
'commands/swap.c', 'commands/swap.c',
'commands/title_format.c', 'commands/title_format.c',
'commands/unmark.c', 'commands/unmark.c',
'commands/urgent.c',
'commands/workspace.c', 'commands/workspace.c',
'commands/workspace_layout.c', 'commands/workspace_layout.c',
'commands/ws_auto_back_and_forth.c', 'commands/ws_auto_back_and_forth.c',

View file

@ -499,6 +499,11 @@ config after the others, or it will be matched instead of the others.
*unmark* will remove _identifier_ from the list of current marks on a *unmark* will remove _identifier_ from the list of current marks on a
window. If _identifier_ is omitted, all marks are removed. window. If _identifier_ is omitted, all marks are removed.
*urgent* enable|disable|allow|deny
Using _enable_ or _disable_ manually sets or unsets the window's urgent
state. Using _allow_ or _deny_ controls the window's ability to set itself
as urgent. By default, windows are allowed to set their own urgency.
*workspace* [number] <name> *workspace* [number] <name>
Switches to the specified workspace. The string "number" is optional and is Switches to the specified workspace. The string "number" is optional and is
used to sort workspaces. used to sort workspaces.

View file

@ -674,16 +674,23 @@ struct sway_container *floating_container_at(double lx, double ly,
void container_for_each_descendant_dfs(struct sway_container *container, void container_for_each_descendant_dfs(struct sway_container *container,
void (*f)(struct sway_container *container, void *data), void (*f)(struct sway_container *container, void *data),
void *data) { void *data) {
if (container) { if (!container) {
if (container->children) { return;
for (int i = 0; i < container->children->length; ++i) {
struct sway_container *child =
container->children->items[i];
container_for_each_descendant_dfs(child, f, data);
}
}
f(container, data);
} }
if (container->children) {
for (int i = 0; i < container->children->length; ++i) {
struct sway_container *child = container->children->items[i];
container_for_each_descendant_dfs(child, f, data);
}
}
if (container->type == C_WORKSPACE) {
struct sway_container *floating = container->sway_workspace->floating;
for (int i = 0; i < floating->children->length; ++i) {
struct sway_container *child = floating->children->items[i];
container_for_each_descendant_dfs(child, f, data);
}
}
f(container, data);
} }
void container_for_each_descendant_bfs(struct sway_container *con, void container_for_each_descendant_bfs(struct sway_container *con,
@ -1063,6 +1070,8 @@ void container_floating_move_to(struct sway_container *con,
container_add_child(new_workspace->sway_workspace->floating, con); container_add_child(new_workspace->sway_workspace->floating, con);
arrange_windows(old_workspace); arrange_windows(old_workspace);
arrange_windows(new_workspace); arrange_windows(new_workspace);
workspace_detect_urgent(old_workspace);
workspace_detect_urgent(new_workspace);
} }
} }
@ -1073,3 +1082,12 @@ void container_set_dirty(struct sway_container *container) {
container->dirty = true; container->dirty = true;
list_add(server.dirty_containers, container); list_add(server.dirty_containers, container);
} }
static bool find_urgent_iterator(struct sway_container *con,
void *data) {
return con->type == C_VIEW && view_is_urgent(con->sway_view);
}
bool container_has_urgent_child(struct sway_container *container) {
return container_find(container, find_urgent_iterator, NULL);
}

View file

@ -225,6 +225,15 @@ void container_move_to(struct sway_container *container,
} }
} }
} }
// Update workspace urgent state
struct sway_container *old_workspace = old_parent;
if (old_workspace->type != C_WORKSPACE) {
old_workspace = container_parent(old_workspace, C_WORKSPACE);
}
if (new_workspace != old_workspace) {
workspace_detect_urgent(new_workspace);
workspace_detect_urgent(old_workspace);
}
} }
static bool sway_dir_to_wlr(enum movement_direction dir, static bool sway_dir_to_wlr(enum movement_direction dir,
@ -548,6 +557,8 @@ void container_move(struct sway_container *container,
} }
if (last_ws && next_ws && last_ws != next_ws) { if (last_ws && next_ws && last_ws != next_ws) {
ipc_event_workspace(last_ws, container, "focus"); ipc_event_workspace(last_ws, container, "focus");
workspace_detect_urgent(last_ws);
workspace_detect_urgent(next_ws);
} }
} }

View file

@ -25,6 +25,7 @@ void view_init(struct sway_view *view, enum sway_view_type type,
view->impl = impl; view->impl = impl;
view->executed_criteria = create_list(); view->executed_criteria = create_list();
view->marks = create_list(); view->marks = create_list();
view->allow_request_urgent = true;
wl_signal_init(&view->events.unmap); wl_signal_init(&view->events.unmap);
} }
@ -609,16 +610,26 @@ void view_unmap(struct sway_view *view) {
wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->surface_new_subsurface.link);
wl_list_remove(&view->container_reparent.link); wl_list_remove(&view->container_reparent.link);
if (view->urgent_timer) {
wl_event_source_remove(view->urgent_timer);
view->urgent_timer = NULL;
}
struct sway_container *parent;
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
if (view->is_fullscreen) { if (view->is_fullscreen) {
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
ws->sway_workspace->fullscreen = NULL; ws->sway_workspace->fullscreen = NULL;
container_destroy(view->swayc); parent = container_destroy(view->swayc);
arrange_windows(ws->parent); arrange_windows(ws->parent);
} else { } else {
struct sway_container *parent = container_destroy(view->swayc); struct sway_container *parent = container_destroy(view->swayc);
arrange_windows(parent); arrange_windows(parent);
} }
if (parent->type >= C_WORKSPACE) { // if the workspace still exists
workspace_detect_urgent(ws);
}
transaction_commit_dirty(); transaction_commit_dirty();
view->surface = NULL; view->surface = NULL;
} }
@ -1067,3 +1078,29 @@ bool view_is_visible(struct sway_view *view) {
} }
return true; return true;
} }
void view_set_urgent(struct sway_view *view, bool enable) {
if (enable) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
if (seat_get_focus(seat) == view->swayc) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &view->urgent);
} else {
view->urgent = (struct timespec){ 0 };
if (view->urgent_timer) {
wl_event_source_remove(view->urgent_timer);
view->urgent_timer = NULL;
}
}
container_damage_whole(view->swayc);
ipc_event_window(view->swayc, "urgent");
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
workspace_detect_urgent(ws);
}
bool view_is_urgent(struct sway_view *view) {
return view->urgent.tv_sec || view->urgent.tv_nsec;
}

View file

@ -11,6 +11,7 @@
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available(
return NULL; return NULL;
} }
void workspace_detect_urgent(struct sway_container *workspace) {
bool new_urgent = container_has_urgent_child(workspace);
if (workspace->sway_workspace->urgent != new_urgent) {
workspace->sway_workspace->urgent = new_urgent;
ipc_event_workspace(NULL, workspace, "urgent");
container_damage_whole(workspace);
}
}

View file

@ -115,6 +115,18 @@ static void ipc_parse_colors(
config->colors.inactive_workspace.text = parse_color( config->colors.inactive_workspace.text = parse_color(
json_object_get_string(inactive_workspace_text)); json_object_get_string(inactive_workspace_text));
} }
if (urgent_workspace_border) {
config->colors.urgent_workspace.border = parse_color(
json_object_get_string(urgent_workspace_border));
}
if (urgent_workspace_bg) {
config->colors.urgent_workspace.background = parse_color(
json_object_get_string(urgent_workspace_bg));
}
if (urgent_workspace_text) {
config->colors.urgent_workspace.text = parse_color(
json_object_get_string(urgent_workspace_text));
}
if (binding_mode_border) { if (binding_mode_border) {
config->colors.binding_mode.border = parse_color( config->colors.binding_mode.border = parse_color(
json_object_get_string(binding_mode_border)); json_object_get_string(binding_mode_border));