aboutsummaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c2
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/floating.c2
-rw-r--r--sway/commands/fullscreen.c2
-rw-r--r--sway/commands/gaps.c8
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/move.c25
-rw-r--r--sway/commands/no_focus.c26
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/resize.c6
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c9
-rw-r--r--sway/commands/urgent.c36
-rw-r--r--sway/config.c43
-rw-r--r--sway/config/output.c2
-rw-r--r--sway/criteria.c44
-rw-r--r--sway/desktop/layer_shell.c3
-rw-r--r--sway/desktop/output.c14
-rw-r--r--sway/desktop/render.c89
-rw-r--r--sway/desktop/transaction.c65
-rw-r--r--sway/desktop/xdg_shell.c8
-rw-r--r--sway/desktop/xdg_shell_v6.c8
-rw-r--r--sway/desktop/xwayland.c12
-rw-r--r--sway/input/cursor.c3
-rw-r--r--sway/input/keyboard.c2
-rw-r--r--sway/input/seat.c41
-rw-r--r--sway/ipc-json.c7
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/main.c18
-rw-r--r--sway/meson.build2
-rw-r--r--sway/server.c7
-rw-r--r--sway/sway.5.scd5
-rw-r--r--sway/tree/arrange.c78
-rw-r--r--sway/tree/container.c58
-rw-r--r--sway/tree/layout.c14
-rw-r--r--sway/tree/view.c78
-rw-r--r--sway/tree/workspace.c13
39 files changed, 492 insertions, 251 deletions
diff --git a/sway/commands.c b/sway/commands.c
index addd64a6..27329602 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -114,6 +114,7 @@ static struct cmd_handler handlers[] = {
{ "input", cmd_input },
{ "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping },
+ { "no_focus", cmd_no_focus },
{ "output", cmd_output },
{ "seat", cmd_seat },
{ "set", cmd_set },
@@ -153,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) {
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index 48e7ddbd..44bb4ae3 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -17,6 +17,7 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
if (strcasecmp(valid[i], argv[0]) == 0) {
wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s",
argv[0], config->current_bar->id);
+ free(config->current_bar->position);
config->current_bar->position = strdup(argv[0]);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 6db85395..9c19e20a 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
container_set_geometry_from_floating_view(view->swayc);
}
- arrange_and_commit(view->swayc);
+ arrange_windows(view->swayc);
struct sway_seat *seat = input_manager_current_seat(input_manager);
if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index e6003521..6ab56c3b 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
container_set_floating(container, wants_floating);
struct sway_container *workspace = container_parent(container, C_WORKSPACE);
- arrange_and_commit(workspace);
+ arrange_windows(workspace);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 1a4d8b41..0b5beaa2 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
view_set_fullscreen(view, wants_fullscreen);
struct sway_container *workspace = container_parent(container, C_WORKSPACE);
- arrange_and_commit(workspace->parent);
+ arrange_windows(workspace->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 801fb179..3906eb70 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "gaps",
"gaps edge_gaps on|off|toggle");
}
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
} else {
int amount_idx = 0; // the current index in argv
enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
if (amount_idx == 0) { // gaps <amount>
config->gaps_inner = val;
config->gaps_outer = val;
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
// Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
} else {
config->gaps_outer = total;
}
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
} else {
struct sway_container *c =
config->handler_context.current_container;
@@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
c->gaps_outer = total;
}
- arrange_and_commit(c->parent ? c->parent : &root_container);
+ arrange_windows(c->parent ? c->parent : &root_container);
}
}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 9945fa5c..c446f1f9 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
}
container_notify_subtree_changed(parent);
- arrange_and_commit(parent);
+ arrange_windows(parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index a1c1e018..6ec050a8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -6,7 +6,6 @@
#include <wlr/types/wlr_output_layout.h>
#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
#include "sway/output.h"
@@ -105,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
- struct sway_transaction *txn = transaction_create();
- arrange_windows(old_ws, txn);
- arrange_windows(destination->parent, txn);
- transaction_commit(txn);
+ arrange_windows(old_ws);
+ arrange_windows(destination->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcasecmp(argv[1], "to") == 0
@@ -144,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
- struct sway_transaction *txn = transaction_create();
- arrange_windows(old_ws, txn);
- arrange_windows(focus->parent, txn);
- transaction_commit(txn);
+ arrange_windows(old_ws);
+ arrange_windows(focus->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -177,10 +172,8 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
}
container_move_to(current, destination);
- struct sway_transaction *txn = transaction_create();
- arrange_windows(source, txn);
- arrange_windows(destination, txn);
- transaction_commit(txn);
+ arrange_windows(source);
+ arrange_windows(destination);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -238,12 +231,10 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
container_move(container, direction, move_amt);
struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
- struct sway_transaction *txn = transaction_create();
- arrange_windows(old_ws, txn);
+ arrange_windows(old_ws);
if (new_ws != old_ws) {
- arrange_windows(new_ws, txn);
+ arrange_windows(new_ws);
}
- transaction_commit(txn);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c
new file mode 100644
index 00000000..61a8de7e
--- /dev/null
+++ b/sway/commands/no_focus.c
@@ -0,0 +1,26 @@
+#define _XOPEN_SOURCE 500
+#include <string.h>
+#include "sway/commands.h"
+#include "sway/criteria.h"
+#include "list.h"
+#include "log.h"
+
+struct cmd_results *cmd_no_focus(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+
+ char *err_str = NULL;
+ struct criteria *criteria = criteria_parse(argv[0], &err_str);
+ if (!criteria) {
+ error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
+ free(err_str);
+ return error;
+ }
+
+ criteria->type = CT_NO_FOCUS;
+ list_add(config->criteria, criteria);
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index c6715f9c..cea6a94b 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -12,6 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
}
load_swaybars();
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 2cf811d8..e657864c 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -268,7 +268,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
}
}
- arrange_and_commit(parent->parent);
+ arrange_windows(parent->parent);
}
/**
@@ -338,7 +338,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
view->height += grow_height;
}
- arrange_and_commit(con);
+ arrange_windows(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@@ -410,7 +410,7 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
view->height += grow_height;
}
- arrange_and_commit(con);
+ arrange_windows(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index f687e78e..7d27e571 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
"Expected 'smart_gaps <on|off>' ");
}
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index c40f4d9f..313799da 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) {
}
struct sway_container *parent = container_split(con, layout);
container_create_notify(parent);
- arrange_and_commit(parent->parent);
+ arrange_windows(parent->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index e052058f..2fc88308 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,7 +1,6 @@
#include <strings.h>
#include <wlr/util/log.h>
#include "sway/commands.h"
-#include "sway/desktop/transaction.h"
#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
#include "sway/tree/view.h"
@@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
container_swap(current, other);
- struct sway_transaction *txn = transaction_create();
- arrange_windows(current->parent, txn);
-
+ arrange_windows(current->parent);
if (other->parent != current->parent) {
- arrange_windows(other->parent, txn);
+ arrange_windows(other->parent);
}
- transaction_commit(txn);
-
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
new file mode 100644
index 00000000..d199858a
--- /dev/null
+++ b/sway/commands/urgent.c
@@ -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);
+}
diff --git a/sway/config.c b/sway/config.c
index d2386f46..f63835bf 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -474,6 +474,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
list_del(config->config_chain, index);
return false;
}
+ free(real_path);
// restore current_config_path
config->current_config_path = parent_config;
@@ -560,16 +561,21 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
bool read_config(FILE *file, struct sway_config *config) {
bool reading_main_config = false;
- char *this_config = NULL, *config_pos;
- long config_size = 0;
+ char *this_config = NULL;
+ size_t config_size = 0;
if (config->current_config == NULL) {
reading_main_config = true;
- fseek(file, 0, SEEK_END);
- config_size = ftell(file);
+ int ret_seek = fseek(file, 0, SEEK_END);
+ long ret_tell = ftell(file);
+ if (ret_seek == -1 || ret_tell == -1) {
+ wlr_log(WLR_ERROR, "Unable to get size of config file");
+ return false;
+ }
+ config_size = ret_tell;
rewind(file);
- config_pos = this_config = malloc(config_size + 1);
+ config->current_config = this_config = calloc(1, config_size + 1);
if (this_config == NULL) {
wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents");
return false;
@@ -580,6 +586,7 @@ bool read_config(FILE *file, struct sway_config *config) {
int line_number = 0;
char *line;
list_t *stack = create_list();
+ size_t read = 0;
while (!feof(file)) {
char *block = stack->length ? stack->items[0] : NULL;
line = read_line(file);
@@ -590,10 +597,21 @@ bool read_config(FILE *file, struct sway_config *config) {
wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
if (reading_main_config) {
- size_t l = strlen(line);
- memcpy(config_pos, line, l); // don't copy terminating character
- config_pos += l;
- *config_pos++ = '\n';
+ size_t length = strlen(line);
+
+ if (read + length > config_size) {
+ wlr_log(WLR_ERROR, "Config file changed during reading");
+ list_foreach(stack, free);
+ list_free(stack);
+ free(line);
+ return false;
+ }
+
+ strcpy(this_config + read, line);
+ if (line_number != 1) {
+ this_config[read - 1] = '\n';
+ }
+ read += length + 1;
}
line = strip_whitespace(line);
@@ -616,7 +634,6 @@ bool read_config(FILE *file, struct sway_config *config) {
list_foreach(stack, free);
list_free(stack);
free(line);
- free(this_config);
return false;
}
wlr_log(WLR_DEBUG, "Expanded line: %s", expanded);
@@ -677,10 +694,6 @@ bool read_config(FILE *file, struct sway_config *config) {
list_foreach(stack, free);
list_free(stack);
- if (reading_main_config) {
- this_config[config_size - 1] = '\0';
- config->current_config = this_config;
- }
return success;
}
@@ -773,6 +786,6 @@ void config_update_font_height(bool recalculate) {
}
if (config->font_height != prev_max_height) {
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
}
}
diff --git a/sway/config/output.c b/sway/config/output.c
index 205e2633..1bf9e5f1 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -207,6 +207,8 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
output->sway_output->bg_pid = fork();
if (output->sway_output->bg_pid == 0) {
execvp(cmd[0], cmd);
+ } else {
+ free(command);
}
}
if (oc && oc->dpms_state != DPMS_IGNORE) {
diff --git a/sway/criteria.c b/sway/criteria.c
index 29a3668b..c999d248 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -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) {
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 16910c7e..91baa6f8 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
-#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
#include "log.h"
@@ -176,7 +175,7 @@ void arrange_layers(struct sway_output *output) {
sizeof(struct wlr_box)) != 0) {
wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
- arrange_and_commit(output->swayc);
+ container_set_dirty(output->swayc);
}
// Arrange non-exlusive surfaces from top->bottom
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a2720885..a9808406 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
output->wlr_output->data = NULL;
free(output);
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
}
static void handle_mode(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, mode);
arrange_layers(output);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
static void handle_transform(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, transform);
arrange_layers(output);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, scale);
arrange_layers(output);
container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) {
output->damage_destroy.notify = damage_handle_destroy;
arrange_layers(output);
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
+ transaction_commit_dirty();
}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index b370f8a2..cb995215 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -15,6 +15,7 @@
#include <wlr/util/region.h>
#include "log.h"
#include "sway/config.h"
+#include "sway/debug.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/layers.h"
@@ -542,9 +543,6 @@ static void render_container(struct sway_output *output,
static void render_container_simple(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con,
bool parent_focused) {
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
-
for (int i = 0; i < con->current.children->length; ++i) {
struct sway_container *child = con->current.children->items[i];
@@ -555,11 +553,15 @@ static void render_container_simple(struct sway_output *output,
struct wlr_texture *marks_texture;
struct sway_container_state *state = &child->current;
- if (focus == child || 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;
- } else if (seat_get_focus_inactive(seat, con) == child) {
+ } else if (con->current.focused_inactive_child == child) {
colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive;
marks_texture = view->marks_focused_inactive;
@@ -579,7 +581,7 @@ static void render_container_simple(struct sway_output *output,
render_view(output, damage, child, colors);
} else {
render_container(output, damage, child,
- parent_focused || focus == child);
+ parent_focused || child->current.focused);
}
}
}
@@ -593,11 +595,9 @@ static void render_container_tabbed(struct sway_output *output,
if (!con->current.children->length) {
return;
}
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- struct sway_container *current = seat_get_active_current_child(seat, con);
- struct border_colors *current_colors = &config->border_colors.unfocused;
struct sway_container_state *pstate = &con->current;
+ struct sway_container *current = pstate->focused_inactive_child;
+ struct border_colors *current_colors = &config->border_colors.unfocused;
double width_gap_adjustment = 2 * pstate->current_gaps;
int tab_width =
@@ -611,12 +611,18 @@ static void render_container_tabbed(struct sway_output *output,
struct border_colors *colors;
struct wlr_texture *title_texture;
struct wlr_texture *marks_texture;
-
- if (focus == child || parent_focused) {
+ bool urgent = view ?
+ view_is_urgent(view) : container_has_urgent_child(child);
+
+ 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;
- } else if (child == current) {
+ } else if (child == pstate->focused_inactive_child) {
colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive;
marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -643,13 +649,11 @@ static void render_container_tabbed(struct sway_output *output,
}
// Render surface and left/right/bottom borders
- if (current) {
- if (current->type == C_VIEW) {
- render_view(output, damage, current, current_colors);
- } else {
- render_container(output, damage, current,
- parent_focused || current == focus);
- }
+ if (current->type == C_VIEW) {
+ render_view(output, damage, current, current_colors);
+ } else {
+ render_container(output, damage, current,
+ parent_focused || current->current.focused);
}
}
@@ -662,11 +666,9 @@ static void render_container_stacked(struct sway_output *output,
if (!con->current.children->length) {
return;
}
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- struct sway_container *current = seat_get_active_current_child(seat, con);
- struct border_colors *current_colors = &config->border_colors.unfocused;
struct sway_container_state *pstate = &con->current;
+ struct sway_container *current = pstate->focused_inactive_child;
+ struct border_colors *current_colors = &config->border_colors.unfocused;
size_t titlebar_height = container_titlebar_height();
@@ -678,12 +680,18 @@ static void render_container_stacked(struct sway_output *output,
struct border_colors *colors;
struct wlr_texture *title_texture;
struct wlr_texture *marks_texture;
-
- if (focus == child || parent_focused) {
+ bool urgent = view ?
+ view_is_urgent(view) : container_has_urgent_child(child);
+
+ 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;
- } else if (child == current) {
+ } else if (child == pstate->focused_inactive_child) {
colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive;
marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -703,13 +711,11 @@ static void render_container_stacked(struct sway_output *output,
}
// Render surface and left/right/bottom borders
- if (current) {
- if (current->type == C_VIEW) {
- render_view(output, damage, current, current_colors);
- } else {
- render_container(output, damage, current,
- parent_focused || current == focus);
- }
+ if (current->type == C_VIEW) {
+ render_view(output, damage, current, current_colors);
+ } else {
+ render_container(output, damage, current,
+ parent_focused || current->current.focused);
}
}
@@ -737,13 +743,15 @@ static void render_floating_container(struct sway_output *soutput,
pixman_region32_t *damage, struct sway_container *con) {
if (con->type == C_VIEW) {
struct sway_view *view = con->sway_view;
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
struct border_colors *colors;
struct wlr_texture *title_texture;
struct wlr_texture *marks_texture;
- if (focus == con) {
+ 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;
@@ -786,6 +794,8 @@ static void render_floating(struct sway_output *soutput,
}
}
+const char *damage_debug = NULL;
+
void output_render(struct sway_output *output, struct timespec *when,
pixman_region32_t *damage) {
struct wlr_output *wlr_output = output->wlr_output;
@@ -805,7 +815,6 @@ void output_render(struct sway_output *output, struct timespec *when,
goto renderer_end;
}
- const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
if (damage_debug != NULL) {
if (strcmp(damage_debug, "highlight") == 0) {
wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
@@ -869,9 +878,7 @@ void output_render(struct sway_output *output, struct timespec *when,
render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- render_container(output, damage, workspace, focus == workspace);
+ render_container(output, damage, workspace, workspace->current.focused);
render_floating(output, damage);
render_unmanaged(output, damage,
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 2b3f87c3..fcfb0b51 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -19,14 +19,14 @@
* How long we should wait for views to respond to the configure before giving
* up and applying the transaction anyway.
*/
-#define TIMEOUT_MS 200
+int txn_timeout_ms = 200;
/**
* If enabled, sway will always wait for the transaction timeout before
* applying it, rather than applying it when the views are ready. This allows us
* to observe the rendered state while a transaction is in progress.
*/
-#define TRANSACTION_DEBUG false
+bool txn_debug = false;
struct sway_transaction {
struct wl_event_source *timer;
@@ -47,7 +47,7 @@ struct sway_transaction_instruction {
bool ready;
};
-struct sway_transaction *transaction_create() {
+static struct sway_transaction *transaction_create() {
struct sway_transaction *transaction =
calloc(1, sizeof(struct sway_transaction));
transaction->instructions = create_list();
@@ -139,25 +139,18 @@ static void copy_pending_state(struct sway_container *container,
state->children = create_list();
list_cat(state->children, container->children);
}
-}
-static bool transaction_has_container(struct sway_transaction *transaction,
- struct sway_container *container) {
- for (int i = 0; i < transaction->instructions->length; ++i) {
- struct sway_transaction_instruction *instruction =
- transaction->instructions->items[i];
- if (instruction->container == container) {
- return true;
- }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ state->focused = seat_get_focus(seat) == container;
+
+ if (container->type != C_VIEW) {
+ state->focused_inactive_child =
+ seat_get_active_child(seat, container);
}
- return false;
}
-void transaction_add_container(struct sway_transaction *transaction,
+static void transaction_add_container(struct sway_transaction *transaction,
struct sway_container *container) {
- if (transaction_has_container(transaction, container)) {
- return;
- }
struct sway_transaction_instruction *instruction =
calloc(1, sizeof(struct sway_transaction_instruction));
instruction->transaction = transaction;
@@ -210,10 +203,12 @@ static void transaction_apply(struct sway_transaction *transaction) {
.width = instruction->state.swayc_width,
.height = instruction->state.swayc_height,
};
- for (int j = 0; j < root_container.children->length; ++j) {
- struct sway_container *output = root_container.children->items[j];
- output_damage_box(output->sway_output, &old_box);
- output_damage_box(output->sway_output, &new_box);
+ for (int j = 0; j < root_container.current.children->length; ++j) {
+ struct sway_container *output = root_container.current.children->items[j];
+ if (output->sway_output) {
+ output_damage_box(output->sway_output, &old_box);
+ output_damage_box(output->sway_output, &new_box);
+ }
}
// There are separate children lists for each instruction state, the
@@ -285,7 +280,7 @@ static bool should_configure(struct sway_container *con,
return true;
}
-void transaction_commit(struct sway_transaction *transaction) {
+static void transaction_commit(struct sway_transaction *transaction) {
wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
transaction, transaction->instructions->length);
transaction->num_waiting = 0;
@@ -330,7 +325,7 @@ void transaction_commit(struct sway_transaction *transaction) {
// Set up a timer which the views must respond within
transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
handle_timeout, transaction);
- wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
+ wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
}
// The debug tree shows the pending/live tree. Here is a good place to
@@ -361,11 +356,11 @@ static void set_instruction_ready(
// If all views are ready, apply the transaction.
// If the transaction has timed out then its num_waiting will be 0 already.
if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
-#if !TRANSACTION_DEBUG
- wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
- wl_event_source_timer_update(transaction->timer, 0);
- transaction_progress_queue();
-#endif
+ if (!txn_debug) {
+ wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
+ wl_event_source_timer_update(transaction->timer, 0);
+ transaction_progress_queue();
+ }
}
}
@@ -418,3 +413,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
*height = instruction->saved_buffer_height;
return instruction->saved_buffer->texture;
}
+
+void transaction_commit_dirty(void) {
+ if (!server.dirty_containers->length) {
+ return;
+ }
+ struct sway_transaction *transaction = transaction_create();
+ for (int i = 0; i < server.dirty_containers->length; ++i) {
+ struct sway_container *container = server.dirty_containers->items[i];
+ transaction_add_container(transaction, container);
+ container->dirty = false;
+ }
+ server.dirty_containers->length = 0;
+ transaction_commit(transaction);
+}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index fbeeb2e3..98c16faf 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -244,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
view_set_fullscreen(view, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
- arrange_and_commit(output);
+ arrange_windows(output);
+ transaction_commit_dirty();
}
static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -281,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xdg_surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
+ arrange_windows(view->swayc->parent);
}
+ transaction_commit_dirty();
xdg_shell_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 88d9bb94..4d76f0a7 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -239,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
view_set_fullscreen(view, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
- arrange_and_commit(output);
+ arrange_windows(output);
+ transaction_commit_dirty();
}
static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -276,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xdg_surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
+ arrange_windows(view->swayc->parent);
}
+ transaction_commit_dirty();
xdg_shell_v6_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 460d1cc8..9df7977d 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -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) {
@@ -333,10 +337,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xsurface->fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
+ arrange_windows(view->swayc->parent);
}
+ transaction_commit_dirty();
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -392,7 +397,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
view_set_fullscreen(view, xsurface->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
- arrange_and_commit(output);
+ arrange_windows(output);
+ transaction_commit_dirty();
}
static void handle_set_title(struct wl_listener *listener, void *data) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 307eedd4..7a9f3ed7 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -10,6 +10,7 @@
#include <wlr/types/wlr_idle.h>
#include "list.h"
#include "log.h"
+#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/layers.h"
#include "sway/output.h"
@@ -219,6 +220,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
struct sway_drag_icon *drag_icon = wlr_drag_icon->data;
drag_icon_update_position(drag_icon);
}
+ transaction_commit_dirty();
}
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@@ -278,6 +280,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
time_msec, button, state);
+ transaction_commit_dirty();
}
static void handle_cursor_button(struct wl_listener *listener, void *data) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 580c0d4b..ede38519 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -3,6 +3,7 @@
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_idle.h>
+#include "sway/desktop/transaction.h"
#include "sway/input/seat.h"
#include "sway/input/keyboard.h"
#include "sway/input/input-manager.h"
@@ -126,6 +127,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
binding->command);
config->handler_context.seat = keyboard->seat_device->sway_seat;
struct cmd_results *results = execute_command(binding->command, NULL);
+ transaction_commit_dirty();
if (results->status != CMD_SUCCESS) {
wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
binding->command, results->error);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 5e65ca70..12b1fab5 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -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) {
@@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
while (parent) {
wl_list_remove(&parent->link);
wl_list_insert(&seat->focus_stack, &parent->link);
+ container_set_dirty(parent->container);
parent =
seat_container_from_container(seat,
@@ -661,9 +668,23 @@ void seat_set_focus_warp(struct sway_seat *seat,
if (last_focus) {
seat_send_unfocus(last_focus, seat);
}
-
seat_send_focus(container, seat);
- container_damage_whole(container->parent);
+
+ container_set_dirty(container);
+ container_set_dirty(container->parent); // for focused_inactive_child
+ if (last_focus) {
+ container_set_dirty(last_focus);
+ }
+ }
+
+ // 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.
@@ -717,10 +738,6 @@ void seat_set_focus_warp(struct sway_seat *seat,
}
}
- if (last_focus) {
- container_damage_whole(last_focus);
- }
-
if (last_workspace && last_workspace != new_workspace) {
cursor_send_pointer_motion(seat->cursor, 0, true);
}
@@ -840,18 +857,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
return NULL;
}
-struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
- struct sway_container *container) {
- struct sway_seat_container *current = NULL;
- wl_list_for_each(current, &seat->focus_stack, link) {
- if (current->container->current.parent == container &&
- current->container->current.layout != L_FLOATING) {
- return current->container;
- }
- }
- return NULL;
-}
-
struct sway_container *seat_get_focus(struct sway_seat *seat) {
if (!seat->has_focus) {
return NULL;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 3d0e88f0..c49ea47e 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -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) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index c5161a6b..be703915 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -18,6 +18,7 @@
#include <wayland-server.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/desktop/transaction.h"
#include "sway/ipc-json.h"
#include "sway/ipc-server.h"
#include "sway/output.h"
@@ -484,6 +485,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
case IPC_COMMAND:
{
struct cmd_results *results = execute_command(buf, NULL);
+ transaction_commit_dirty();
char *json = cmd_results_to_json(results);
int length = strlen(json);
client_valid = ipc_send_reply(client, json, (uint32_t)length);
diff --git a/sway/main.c b/sway/main.c
index c6453226..1a55b519 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -20,6 +20,7 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/debug.h"
+#include "sway/desktop/transaction.h"
#include "sway/server.h"
#include "sway/tree/layout.h"
#include "sway/ipc-server.h"
@@ -251,6 +252,18 @@ static void drop_permissions(bool keep_caps) {
#endif
}
+void enable_debug_flag(const char *flag) {
+ if (strcmp(flag, "render-tree") == 0) {
+ enable_debug_tree = true;
+ } else if (strncmp(flag, "damage=", 7) == 0) {
+ damage_debug = &flag[7];
+ } else if (strcmp(flag, "txn-debug") == 0) {
+ txn_debug = true;
+ } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
+ txn_timeout_ms = atoi(&flag[12]);
+ }
+}
+
int main(int argc, char **argv) {
static int verbose = 0, debug = 0, validate = 0;
@@ -290,7 +303,7 @@ int main(int argc, char **argv) {
int c;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index);
+ c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index);
if (c == -1) {
break;
}
@@ -309,7 +322,7 @@ int main(int argc, char **argv) {
debug = 1;
break;
case 'D': // extended debug options
- enable_debug_tree = true;
+ enable_debug_flag(optarg);
break;
case 'v': // version
fprintf(stdout, "sway version " SWAY_VERSION "\n");
@@ -429,6 +442,7 @@ int main(int argc, char **argv) {
free(line);
list_del(config->cmd_queue, 0);
}
+ transaction_commit_dirty();
if (!terminate_request) {
server_run(&server);
diff --git a/sway/meson.build b/sway/meson.build
index f878450d..23e54a62 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -59,6 +59,7 @@ sway_sources = files(
'commands/mode.c',
'commands/mouse_warping.c',
'commands/move.c',
+ 'commands/no_focus.c',
'commands/output.c',
'commands/reload.c',
'commands/rename.c',
@@ -76,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',
diff --git a/sway/server.c b/sway/server.c
index 1d8eb964..f904b177 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -14,7 +14,6 @@
#include <wlr/types/wlr_linux_dmabuf.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_screencopy_v1.h>
-#include <wlr/types/wlr_screenshooter.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_output.h>
@@ -53,7 +52,6 @@ bool server_init(struct sway_server *server) {
server->data_device_manager =
wlr_data_device_manager_create(server->wl_display);
- wlr_screenshooter_create(server->wl_display);
wlr_gamma_control_manager_create(server->wl_display);
wlr_primary_selection_device_manager_create(server->wl_display);
@@ -125,8 +123,7 @@ bool server_init(struct sway_server *server) {
if (debug != NULL && strcmp(debug, "txn_timings") == 0) {
server->debug_txn_timings = true;
}
- server->destroying_containers = create_list();
-
+ server->dirty_containers = create_list();
server->transactions = create_list();
input_manager = input_manager_create(server);
@@ -136,7 +133,7 @@ bool server_init(struct sway_server *server) {
void server_fini(struct sway_server *server) {
// TODO: free sway-specific resources
wl_display_destroy(server->wl_display);
- list_free(server->destroying_containers);
+ list_free(server->dirty_containers);
list_free(server->transactions);
}
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index c6eb5e6d..d369d7b6 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -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.
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bcc3ee9a..533cf71c 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -144,38 +144,22 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
}
}
-/**
- * If a container has been deleted from the pending tree state, we must add it
- * to the transaction so it can be freed afterwards. To do this, we iterate the
- * server's destroying_containers list and add all of them. We may add more than
- * what we need to, but this is easy and has no negative consequences.
- */
-static void add_deleted_containers(struct sway_transaction *transaction) {
- for (int i = 0; i < server.destroying_containers->length; ++i) {
- struct sway_container *child = server.destroying_containers->items[i];
- transaction_add_container(transaction, child);
- }
-}
-
-static void arrange_children_of(struct sway_container *parent,
- struct sway_transaction *transaction);
+static void arrange_children_of(struct sway_container *parent);
-static void arrange_floating(struct sway_container *floating,
- struct sway_transaction *transaction) {
+static void arrange_floating(struct sway_container *floating) {
for (int i = 0; i < floating->children->length; ++i) {
struct sway_container *floater = floating->children->items[i];
if (floater->type == C_VIEW) {
view_autoconfigure(floater->sway_view);
} else {
- arrange_children_of(floater, transaction);
+ arrange_children_of(floater);
}
- transaction_add_container(transaction, floater);
+ container_set_dirty(floater);
}
- transaction_add_container(transaction, floating);
+ container_set_dirty(floating);
}
-static void arrange_children_of(struct sway_container *parent,
- struct sway_transaction *transaction) {
+static void arrange_children_of(struct sway_container *parent) {
if (config->reloading) {
return;
}
@@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent,
apply_horiz_layout(parent);
break;
case L_FLOATING:
- arrange_floating(parent, transaction);
+ arrange_floating(parent);
break;
}
@@ -213,14 +197,13 @@ static void arrange_children_of(struct sway_container *parent,
if (child->type == C_VIEW) {
view_autoconfigure(child->sway_view);
} else {
- arrange_children_of(child, transaction);
+ arrange_children_of(child);
}
- transaction_add_container(transaction, child);
+ container_set_dirty(child);
}
}
-static void arrange_workspace(struct sway_container *workspace,
- struct sway_transaction *transaction) {
+static void arrange_workspace(struct sway_container *workspace) {
if (config->reloading) {
return;
}
@@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace,
workspace->x = output->x + area->x;
workspace->y = output->y + area->y;
add_gaps(workspace);
- transaction_add_container(transaction, workspace);
+ container_set_dirty(workspace);
wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
workspace->x, workspace->y);
- arrange_floating(workspace->sway_workspace->floating, transaction);
- arrange_children_of(workspace, transaction);
+ arrange_floating(workspace->sway_workspace->floating);
+ arrange_children_of(workspace);
}
-static void arrange_output(struct sway_container *output,
- struct sway_transaction *transaction) {
+static void arrange_output(struct sway_container *output) {
if (config->reloading) {
return;
}
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,
output->y = output_box->y;
output->width = output_box->width;
output->height = output_box->height;
- transaction_add_container(transaction, output);
+ container_set_dirty(output);
wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
output->name, output->x, output->y);
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
- arrange_workspace(workspace, transaction);
+ arrange_workspace(workspace);
}
}
-static void arrange_root(struct sway_transaction *transaction) {
+static void arrange_root() {
if (config->reloading) {
return;
}
@@ -274,43 +256,35 @@ static void arrange_root(struct sway_transaction *transaction) {
root_container.y = layout_box->y;
root_container.width = layout_box->width;
root_container.height = layout_box->height;
- transaction_add_container(transaction, &root_container);
+ container_set_dirty(&root_container);
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *output = root_container.children->items[i];
- arrange_output(output, transaction);
+ arrange_output(output);
}
}
-void arrange_windows(struct sway_container *container,
- struct sway_transaction *transaction) {
+void arrange_windows(struct sway_container *container) {
switch (container->type) {
case C_ROOT:
- arrange_root(transaction);
+ arrange_root();
break;
case C_OUTPUT:
- arrange_output(container, transaction);
+ arrange_output(container);
break;
case C_WORKSPACE:
- arrange_workspace(container, transaction);
+ arrange_workspace(container);
break;
case C_CONTAINER:
- arrange_children_of(container, transaction);
- transaction_add_container(transaction, container);
+ arrange_children_of(container);
+ container_set_dirty(container);
break;
case C_VIEW:
view_autoconfigure(container->sway_view);
- transaction_add_container(transaction, container);
+ container_set_dirty(container);
break;
case C_TYPES:
break;
}
- add_deleted_containers(transaction);
-}
-
-void arrange_and_commit(struct sway_container *container) {
- struct sway_transaction *transaction = transaction_create();
- arrange_windows(container, transaction);
- transaction_commit(transaction);
}
void remove_gaps(struct sway_container *c) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 58852717..6d52c38c 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -159,14 +159,6 @@ void container_free(struct sway_container *cont) {
wlr_texture_destroy(cont->title_focused_inactive);
wlr_texture_destroy(cont->title_unfocused);
wlr_texture_destroy(cont->title_urgent);
-
- for (int i = 0; i < server.destroying_containers->length; ++i) {
- if (server.destroying_containers->items[i] == cont) {
- list_del(server.destroying_containers, i);
- break;
- }
- }
-
list_free(cont->instructions);
list_free(cont->children);
list_free(cont->current.children);
@@ -325,7 +317,7 @@ static struct sway_container *container_destroy_noreaping(
}
con->destroying = true;
- list_add(server.destroying_containers, con);
+ container_set_dirty(con);
if (!con->parent) {
return NULL;
@@ -682,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);
- }
+ 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);
}
+ f(container, data);
}
void container_for_each_descendant_bfs(struct sway_container *con,
@@ -1069,9 +1068,26 @@ void container_floating_move_to(struct sway_container *con,
if (old_workspace != new_workspace) {
container_remove_child(con);
container_add_child(new_workspace->sway_workspace->floating, con);
- struct sway_transaction *transaction = transaction_create();
- arrange_windows(old_workspace, transaction);
- arrange_windows(new_workspace, transaction);
- transaction_commit(transaction);
+ arrange_windows(old_workspace);
+ arrange_windows(new_workspace);
+ workspace_detect_urgent(old_workspace);
+ workspace_detect_urgent(new_workspace);
}
}
+
+void container_set_dirty(struct sway_container *container) {
+ if (container->dirty) {
+ return;
+ }
+ 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);
+}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ba234e89..197a2fc8 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -22,7 +22,8 @@ struct sway_container root_container;
static void output_layout_handle_change(struct wl_listener *listener,
void *data) {
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
+ transaction_commit_dirty();
}
void layout_init(void) {
@@ -224,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,
@@ -547,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);
}
}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index b356183c..76e0f42c 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -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);
}
@@ -504,20 +505,38 @@ void view_execute_criteria(struct sway_view *view) {
}
wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
criteria->raw, view, criteria->cmdlist);
+ seat_set_focus(seat, view->swayc);
list_add(view->executed_criteria, criteria);
struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
if (res->status != CMD_SUCCESS) {
wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);
}
free_cmd_results(res);
- // view must be focused for commands to affect it,
- // so always refocus in-between command lists
- seat_set_focus(seat, view->swayc);
}
list_free(criterias);
seat_set_focus(seat, prior_focus);
}
+static bool should_focus(struct sway_view *view) {
+ // If the view is the only one in the focused workspace, it'll get focus
+ // regardless of any no_focus criteria.
+ struct sway_container *parent = view->swayc->parent;
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) {
+ size_t num_children = parent->children->length +
+ parent->sway_workspace->floating->children->length;
+ if (num_children == 1) {
+ return true;
+ }
+ }
+
+ // Check no_focus criteria
+ list_t *criterias = criteria_for_view(view, CT_NO_FOCUS);
+ size_t len = criterias->length;
+ list_free(criterias);
+ return len == 0;
+}
+
void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
return;
@@ -571,9 +590,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view_set_tiled(view, true);
}
- input_manager_set_focus(input_manager, cont);
- if (workspace) {
- workspace_switch(workspace);
+ if (should_focus(view)) {
+ input_manager_set_focus(input_manager, cont);
+ if (workspace) {
+ workspace_switch(workspace);
+ }
}
view_update_title(view, false);
@@ -589,16 +610,27 @@ 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_and_commit(ws->parent);
+ arrange_windows(ws->parent);
} else {
struct sway_container *parent = container_destroy(view->swayc);
- arrange_and_commit(parent);
+ arrange_windows(parent);
}
+ if (parent->type >= C_WORKSPACE) { // if the workspace still exists
+ workspace_detect_urgent(ws);
+ }
+ transaction_commit_dirty();
view->surface = NULL;
}
@@ -1046,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;
+}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 50f9400a..622f01ec 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -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"
@@ -427,7 +428,7 @@ bool workspace_switch(struct sway_container *workspace) {
}
seat_set_focus(seat, next);
struct sway_container *output = container_parent(workspace, C_OUTPUT);
- arrange_and_commit(output);
+ arrange_windows(output);
return true;
}
@@ -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);
+ }
+}