mirror of
https://github.com/swaywm/sway.git
synced 2024-12-28 07:56:31 +01:00
Merge pull request #2276 from RyanDwyer/urgency
Implement urgency base functionality
This commit is contained in:
commit
d6bd314dff
18 changed files with 250 additions and 18 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
36
sway/commands/urgent.c
Normal 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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue