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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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_title_format;
sway_cmd cmd_unmark;
sway_cmd cmd_urgent;
sway_cmd cmd_workspace;
sway_cmd cmd_ws_auto_back_and_forth;
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);
bool container_has_urgent_child(struct sway_container *container);
#endif

View file

@ -70,6 +70,10 @@ struct sway_view {
bool border_left;
bool border_right;
struct timespec urgent;
bool allow_request_urgent;
struct wl_event_source *urgent_timer;
bool destroying;
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);
void view_set_urgent(struct sway_view *view, bool enable);
bool view_is_urgent(struct sway_view *view);
#endif

View file

@ -10,6 +10,7 @@ struct sway_workspace {
struct sway_view *fullscreen;
struct sway_container *floating;
list_t *output_priority;
bool urgent;
};
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 *ws, struct sway_container *exclude);
void workspace_detect_urgent(struct sway_container *workspace);
#endif

View file

@ -154,6 +154,7 @@ static struct cmd_handler command_handlers[] = {
{ "swap", cmd_swap },
{ "title_format", cmd_title_format },
{ "unmark", cmd_unmark },
{ "urgent", cmd_urgent },
};
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);
}
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,
struct sway_view *view) {
if (criteria->title) {
@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria,
}
if (criteria->urgent) {
// TODO
return false;
if (!view_is_urgent(view)) {
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) {

View file

@ -553,7 +553,11 @@ static void render_container_simple(struct sway_output *output,
struct wlr_texture *marks_texture;
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;
title_texture = child->title_focused;
marks_texture = view->marks_focused;
@ -607,8 +611,14 @@ static void render_container_tabbed(struct sway_output *output,
struct border_colors *colors;
struct wlr_texture *title_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;
title_texture = child->title_focused;
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 wlr_texture *title_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;
title_texture = child->title_focused;
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 *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;
title_texture = con->title_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);
if (view->allow_request_urgent) {
view_set_urgent(view, (bool)xsurface->hints_urgency);
}
}
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,
struct sway_container *container, bool warp) {
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.
// We do this by putting it at the end of the floating list.
// 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_new_string(workspace->parent->name) : NULL);
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_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_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) {

View file

@ -77,6 +77,7 @@ sway_sources = files(
'commands/swap.c',
'commands/title_format.c',
'commands/unmark.c',
'commands/urgent.c',
'commands/workspace.c',
'commands/workspace_layout.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
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>
Switches to the specified workspace. The string "number" is optional and is
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 (*f)(struct sway_container *container, void *data),
void *data) {
if (container) {
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);
}
}
f(container, data);
if (!container) {
return;
}
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,
@ -1063,6 +1070,8 @@ void container_floating_move_to(struct sway_container *con,
container_add_child(new_workspace->sway_workspace->floating, con);
arrange_windows(old_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;
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,
@ -548,6 +557,8 @@ void container_move(struct sway_container *container,
}
if (last_ws && next_ws && last_ws != next_ws) {
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->executed_criteria = create_list();
view->marks = create_list();
view->allow_request_urgent = true;
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->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) {
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
ws->sway_workspace->fullscreen = NULL;
container_destroy(view->swayc);
parent = container_destroy(view->swayc);
arrange_windows(ws->parent);
} else {
struct sway_container *parent = container_destroy(view->swayc);
arrange_windows(parent);
}
if (parent->type >= C_WORKSPACE) { // if the workspace still exists
workspace_detect_urgent(ws);
}
transaction_commit_dirty();
view->surface = NULL;
}
@ -1067,3 +1078,29 @@ bool view_is_visible(struct sway_view *view) {
}
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/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "list.h"
#include "log.h"
@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available(
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(
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) {
config->colors.binding_mode.border = parse_color(
json_object_get_string(binding_mode_border));