summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <RyanDwyer@users.noreply.github.com>2018-07-18 09:32:03 +1000
committerLibravatar GitHub <noreply@github.com>2018-07-18 09:32:03 +1000
commit8ce7e3b44eea0a270ecc35a9da2ae801aaf6bce1 (patch)
tree6badffb0c6ee33b4e23e914c4c9f9b39a625b5f3
parentDestroy empty workspace when destroying its output (diff)
parentMerge pull request #2281 from pvsr/X11_click (diff)
downloadsway-8ce7e3b44eea0a270ecc35a9da2ae801aaf6bce1.tar.gz
sway-8ce7e3b44eea0a270ecc35a9da2ae801aaf6bce1.tar.zst
sway-8ce7e3b44eea0a270ecc35a9da2ae801aaf6bce1.zip
Merge branch 'master' into destroy-output-destroy-empty-workspaces
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--HACKING.md6
-rw-r--r--common/background-image.c8
-rw-r--r--common/ipc-client.c2
-rw-r--r--common/list.c15
-rw-r--r--common/log.c4
-rw-r--r--common/pango.c2
-rw-r--r--common/readline.c4
-rw-r--r--common/util.c2
-rw-r--r--completions/zsh/_swaymsg1
-rw-r--r--include/ipc.h5
-rw-r--r--include/list.h2
-rw-r--r--include/log.h10
-rw-r--r--include/sway/commands.h4
-rw-r--r--include/sway/config.h11
-rw-r--r--include/sway/criteria.h5
-rw-r--r--include/sway/debug.h8
-rw-r--r--include/sway/desktop.h4
-rw-r--r--include/sway/desktop/transaction.h33
-rw-r--r--include/sway/input/seat.h11
-rw-r--r--include/sway/server.h5
-rw-r--r--include/sway/tree/arrange.h22
-rw-r--r--include/sway/tree/container.h21
-rw-r--r--include/sway/tree/view.h10
-rw-r--r--include/sway/tree/workspace.h4
-rw-r--r--include/swaybar/bar.h15
-rw-r--r--include/swaybar/status_line.h4
-rw-r--r--include/swaylock/swaylock.h24
-rw-r--r--sway/commands.c21
-rw-r--r--sway/commands/assign.c2
-rw-r--r--sway/commands/bar.c6
-rw-r--r--sway/commands/bar/binding_mode_indicator.c4
-rw-r--r--sway/commands/bar/font.c2
-rw-r--r--sway/commands/bar/height.c2
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/id.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/bar/modifier.c2
-rw-r--r--sway/commands/bar/output.c2
-rw-r--r--sway/commands/bar/pango_markup.c4
-rw-r--r--sway/commands/bar/position.c3
-rw-r--r--sway/commands/bar/separator_symbol.c2
-rw-r--r--sway/commands/bar/status_command.c2
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c4
-rw-r--r--sway/commands/bar/swaybar_command.c2
-rw-r--r--sway/commands/bar/workspace_buttons.c4
-rw-r--r--sway/commands/bar/wrap_scroll.c4
-rw-r--r--sway/commands/bind.c4
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/default_floating_border.c29
-rw-r--r--sway/commands/exec.c2
-rw-r--r--sway/commands/exec_always.c8
-rw-r--r--sway/commands/floating.c2
-rw-r--r--sway/commands/floating_minmax_size.c53
-rw-r--r--sway/commands/focus.c32
-rw-r--r--sway/commands/for_window.c2
-rw-r--r--sway/commands/fullscreen.c2
-rw-r--r--sway/commands/gaps.c8
-rw-r--r--sway/commands/input.c4
-rw-r--r--sway/commands/input/events.c2
-rw-r--r--sway/commands/input/scroll_button.c44
-rw-r--r--sway/commands/input/tap.c2
-rw-r--r--sway/commands/input/tap_button_map.c33
-rw-r--r--sway/commands/input/xkb_layout.c2
-rw-r--r--sway/commands/input/xkb_model.c2
-rw-r--r--sway/commands/input/xkb_options.c2
-rw-r--r--sway/commands/input/xkb_rules.c2
-rw-r--r--sway/commands/input/xkb_variant.c2
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/mode.c2
-rw-r--r--sway/commands/move.c147
-rw-r--r--sway/commands/no_focus.c26
-rw-r--r--sway/commands/output.c6
-rw-r--r--sway/commands/output/background.c8
-rw-r--r--sway/commands/reload.c4
-rw-r--r--sway/commands/rename.c2
-rw-r--r--sway/commands/resize.c410
-rw-r--r--sway/commands/set.c2
-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/swaybg_command.c2
-rw-r--r--sway/commands/urgent.c36
-rw-r--r--sway/commands/workspace.c2
-rw-r--r--sway/config.c127
-rw-r--r--sway/config/bar.c14
-rw-r--r--sway/config/input.c16
-rw-r--r--sway/config/output.c28
-rw-r--r--sway/config/seat.c6
-rw-r--r--sway/criteria.c48
-rw-r--r--sway/desktop/desktop.c9
-rw-r--r--sway/desktop/idle_inhibit_v1.c5
-rw-r--r--sway/desktop/layer_shell.c21
-rw-r--r--sway/desktop/output.c24
-rw-r--r--sway/desktop/render.c150
-rw-r--r--sway/desktop/transaction.c77
-rw-r--r--sway/desktop/xdg_shell.c33
-rw-r--r--sway/desktop/xdg_shell_v6.c33
-rw-r--r--sway/desktop/xwayland.c31
-rw-r--r--sway/input/cursor.c5
-rw-r--r--sway/input/input-manager.c50
-rw-r--r--sway/input/keyboard.c10
-rw-r--r--sway/input/seat.c73
-rw-r--r--sway/ipc-json.c9
-rw-r--r--sway/ipc-server.c90
-rw-r--r--sway/main.c68
-rw-r--r--sway/meson.build6
-rw-r--r--sway/server.c19
-rw-r--r--sway/sway-input.5.scd11
-rw-r--r--sway/sway.5.scd11
-rw-r--r--sway/tree/arrange.c102
-rw-r--r--sway/tree/container.c163
-rw-r--r--sway/tree/layout.c86
-rw-r--r--sway/tree/output.c6
-rw-r--r--sway/tree/view.c159
-rw-r--r--sway/tree/workspace.c27
-rw-r--r--swaybar/bar.c19
-rw-r--r--swaybar/i3bar.c36
-rw-r--r--swaybar/ipc.c16
-rw-r--r--swaybar/main.c8
-rw-r--r--swaybar/render.c7
-rw-r--r--swaybar/status_line.c6
-rw-r--r--swaybg/main.c9
-rw-r--r--swayidle/main.c93
-rw-r--r--swaylock/main.c462
-rw-r--r--swaylock/password.c10
-rw-r--r--swaylock/render.c74
-rw-r--r--swaylock/seat.c4
-rw-r--r--swaylock/swaylock.1.scd95
-rw-r--r--swaymsg/main.c51
-rw-r--r--swaymsg/swaymsg.1.scd10
131 files changed, 2642 insertions, 992 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8a6c0208..f450563a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -107,7 +107,7 @@ int main(int argc, const char **argv) {
107 } 107 }
108 108
109 int desired_output = atoi(argv[1]); 109 int desired_output = atoi(argv[1]);
110 sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); 110 sway_log(WLR_INFO, "Using output %d of %d", desired_output, registry->outputs->length);
111 int i; 111 int i;
112 struct output_state *output = registry->outputs->items[desired_output]; 112 struct output_state *output = registry->outputs->items[desired_output];
113 struct window *window = window_setup(registry, 100, 100, false); 113 struct window *window = window_setup(registry, 100, 100, false);
diff --git a/HACKING.md b/HACKING.md
index 3600db24..8965d3ec 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -3,9 +3,9 @@
3Use `sway_log(importance, fmt, ...)` to log. The following importances are 3Use `sway_log(importance, fmt, ...)` to log. The following importances are
4available: 4available:
5 5
6* `L_DEBUG`: Debug messages, only shows with `sway -d` 6* `WLR_DEBUG`: Debug messages, only shows with `sway -d`
7* `L_INFO`: Informational messages 7* `WLR_INFO`: Informational messages
8* `L_ERROR`: Error messages 8* `WLR_ERROR`: Error messages
9 9
10`sway_log` is a macro that calls `_sway_log` with the current filename and line 10`sway_log` is a macro that calls `_sway_log` with the current filename and line
11number, which are written into the log with your message. 11number, which are written into the log with your message.
diff --git a/common/background-image.c b/common/background-image.c
index e5fb4433..f3d2551e 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -18,7 +18,7 @@ enum background_mode parse_background_mode(const char *mode) {
18 } else if (strcmp(mode, "solid_color") == 0) { 18 } else if (strcmp(mode, "solid_color") == 0) {
19 return BACKGROUND_MODE_SOLID_COLOR; 19 return BACKGROUND_MODE_SOLID_COLOR;
20 } 20 }
21 wlr_log(L_ERROR, "Unsupported background mode: %s", mode); 21 wlr_log(WLR_ERROR, "Unsupported background mode: %s", mode);
22 return BACKGROUND_MODE_INVALID; 22 return BACKGROUND_MODE_INVALID;
23} 23}
24 24
@@ -28,7 +28,7 @@ cairo_surface_t *load_background_image(const char *path) {
28 GError *err = NULL; 28 GError *err = NULL;
29 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); 29 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
30 if (!pixbuf) { 30 if (!pixbuf) {
31 wlr_log(L_ERROR, "Failed to load background image (%s).", 31 wlr_log(WLR_ERROR, "Failed to load background image (%s).",
32 err->message); 32 err->message);
33 return false; 33 return false;
34 } 34 }
@@ -38,11 +38,11 @@ cairo_surface_t *load_background_image(const char *path) {
38 image = cairo_image_surface_create_from_png(path); 38 image = cairo_image_surface_create_from_png(path);
39#endif //HAVE_GDK_PIXBUF 39#endif //HAVE_GDK_PIXBUF
40 if (!image) { 40 if (!image) {
41 wlr_log(L_ERROR, "Failed to read background image."); 41 wlr_log(WLR_ERROR, "Failed to read background image.");
42 return NULL; 42 return NULL;
43 } 43 }
44 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { 44 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
45 wlr_log(L_ERROR, "Failed to read background image: %s." 45 wlr_log(WLR_ERROR, "Failed to read background image: %s."
46#ifndef HAVE_GDK_PIXBUF 46#ifndef HAVE_GDK_PIXBUF
47 "\nSway was compiled without gdk_pixbuf support, so only" 47 "\nSway was compiled without gdk_pixbuf support, so only"
48 "\nPNG images can be loaded. This is the likely cause." 48 "\nPNG images can be loaded. This is the likely cause."
diff --git a/common/ipc-client.c b/common/ipc-client.c
index a88df080..4d2d88cc 100644
--- a/common/ipc-client.c
+++ b/common/ipc-client.c
@@ -97,7 +97,7 @@ struct ipc_response *ipc_recv_response(int socketfd) {
97error_2: 97error_2:
98 free(response); 98 free(response);
99error_1: 99error_1:
100 wlr_log(L_ERROR, "Unable to allocate memory for IPC response"); 100 wlr_log(WLR_ERROR, "Unable to allocate memory for IPC response");
101 return NULL; 101 return NULL;
102} 102}
103 103
diff --git a/common/list.c b/common/list.c
index 39cc10e1..66d52f70 100644
--- a/common/list.c
+++ b/common/list.c
@@ -2,6 +2,7 @@
2#include <stdio.h> 2#include <stdio.h>
3#include <stdlib.h> 3#include <stdlib.h>
4#include <string.h> 4#include <string.h>
5#include "log.h"
5 6
6list_t *create_list(void) { 7list_t *create_list(void) {
7 list_t *list = malloc(sizeof(list_t)); 8 list_t *list = malloc(sizeof(list_t));
@@ -82,6 +83,20 @@ void list_swap(list_t *list, int src, int dest) {
82 list->items[dest] = tmp; 83 list->items[dest] = tmp;
83} 84}
84 85
86void list_move_to_end(list_t *list, void *item) {
87 int i;
88 for (i = 0; i < list->length; ++i) {
89 if (list->items[i] == item) {
90 break;
91 }
92 }
93 if (!sway_assert(i < list->length, "Item not found in list")) {
94 return;
95 }
96 list_del(list, i);
97 list_add(list, item);
98}
99
85static void list_rotate(list_t *list, int from, int to) { 100static void list_rotate(list_t *list, int from, int to) {
86 void *tmp = list->items[to]; 101 void *tmp = list->items[to];
87 102
diff --git a/common/log.c b/common/log.c
index 2cc7289c..847f3952 100644
--- a/common/log.c
+++ b/common/log.c
@@ -8,7 +8,7 @@ void sway_terminate(int code);
8void _sway_abort(const char *format, ...) { 8void _sway_abort(const char *format, ...) {
9 va_list args; 9 va_list args;
10 va_start(args, format); 10 va_start(args, format);
11 _wlr_vlog(L_ERROR, format, args); 11 _wlr_vlog(WLR_ERROR, format, args);
12 va_end(args); 12 va_end(args);
13 sway_terminate(EXIT_FAILURE); 13 sway_terminate(EXIT_FAILURE);
14} 14}
@@ -20,7 +20,7 @@ bool _sway_assert(bool condition, const char *format, ...) {
20 20
21 va_list args; 21 va_list args;
22 va_start(args, format); 22 va_start(args, format);
23 _wlr_vlog(L_ERROR, format, args); 23 _wlr_vlog(WLR_ERROR, format, args);
24 va_end(args); 24 va_end(args);
25 25
26#ifndef NDEBUG 26#ifndef NDEBUG
diff --git a/common/pango.c b/common/pango.c
index c88e50ce..92703f80 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -81,7 +81,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
81 pango_layout_set_markup(layout, buf, -1); 81 pango_layout_set_markup(layout, buf, -1);
82 free(buf); 82 free(buf);
83 } else { 83 } else {
84 wlr_log(L_ERROR, "pango_parse_markup '%s' -> error %s", text, 84 wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text,
85 error->message); 85 error->message);
86 g_error_free(error); 86 g_error_free(error);
87 markup = false; // fallback to plain text 87 markup = false; // fallback to plain text
diff --git a/common/readline.c b/common/readline.c
index 1c396a90..a2c69018 100644
--- a/common/readline.c
+++ b/common/readline.c
@@ -9,7 +9,7 @@ char *read_line(FILE *file) {
9 char *string = malloc(size); 9 char *string = malloc(size);
10 char lastChar = '\0'; 10 char lastChar = '\0';
11 if (!string) { 11 if (!string) {
12 wlr_log(L_ERROR, "Unable to allocate memory for read_line"); 12 wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");
13 return NULL; 13 return NULL;
14 } 14 }
15 while (1) { 15 while (1) {
@@ -30,7 +30,7 @@ char *read_line(FILE *file) {
30 char *new_string = realloc(string, size *= 2); 30 char *new_string = realloc(string, size *= 2);
31 if (!new_string) { 31 if (!new_string) {
32 free(string); 32 free(string);
33 wlr_log(L_ERROR, "Unable to allocate memory for read_line"); 33 wlr_log(WLR_ERROR, "Unable to allocate memory for read_line");
34 return NULL; 34 return NULL;
35 } 35 }
36 string = new_string; 36 string = new_string;
diff --git a/common/util.c b/common/util.c
index 678926ed..e8a88772 100644
--- a/common/util.c
+++ b/common/util.c
@@ -113,7 +113,7 @@ uint32_t parse_color(const char *color) {
113 113
114 int len = strlen(color); 114 int len = strlen(color);
115 if (len != 6 && len != 8) { 115 if (len != 6 && len != 8) {
116 wlr_log(L_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color); 116 wlr_log(WLR_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
117 return 0xFFFFFFFF; 117 return 0xFFFFFFFF;
118 } 118 }
119 uint32_t res = (uint32_t)strtoul(color, NULL, 16); 119 uint32_t res = (uint32_t)strtoul(color, NULL, 16);
diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg
index 6bb03279..2e39deb6 100644
--- a/completions/zsh/_swaymsg
+++ b/completions/zsh/_swaymsg
@@ -22,7 +22,6 @@ types=(
22'get_marks' 22'get_marks'
23'get_bar_config' 23'get_bar_config'
24'get_version' 24'get_version'
25'get_clipboard'
26) 25)
27 26
28_arguments -s \ 27_arguments -s \
diff --git a/include/ipc.h b/include/ipc.h
index 8172c782..0010718b 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -13,11 +13,12 @@ enum ipc_command_type {
13 IPC_GET_MARKS = 5, 13 IPC_GET_MARKS = 5,
14 IPC_GET_BAR_CONFIG = 6, 14 IPC_GET_BAR_CONFIG = 6,
15 IPC_GET_VERSION = 7, 15 IPC_GET_VERSION = 7,
16 IPC_GET_BINDING_MODES = 8,
17 IPC_GET_CONFIG = 9,
16 18
17 // sway-specific command types 19 // sway-specific command types
18 IPC_GET_INPUTS = 100, 20 IPC_GET_INPUTS = 100,
19 IPC_GET_CLIPBOARD = 101, 21 IPC_GET_SEATS = 101,
20 IPC_GET_SEATS = 102,
21 22
22 // Events sent from sway to clients. Events have the highest bits set. 23 // Events sent from sway to clients. Events have the highest bits set.
23 IPC_EVENT_WORKSPACE = ((1<<31) | 0), 24 IPC_EVENT_WORKSPACE = ((1<<31) | 0),
diff --git a/include/list.h b/include/list.h
index 7eead4ac..5a0d7d80 100644
--- a/include/list.h
+++ b/include/list.h
@@ -24,4 +24,6 @@ int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to
24void list_stable_sort(list_t *list, int compare(const void *a, const void *b)); 24void list_stable_sort(list_t *list, int compare(const void *a, const void *b));
25// swap two elements in a list 25// swap two elements in a list
26void list_swap(list_t *list, int src, int dest); 26void list_swap(list_t *list, int src, int dest);
27// move item to end of list
28void list_move_to_end(list_t *list, void *item);
27#endif 29#endif
diff --git a/include/log.h b/include/log.h
index a9748127..dd526143 100644
--- a/include/log.h
+++ b/include/log.h
@@ -3,13 +3,19 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include <wlr/util/log.h> 4#include <wlr/util/log.h>
5 5
6#ifdef __GNUC__
7#define ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end)))
8#else
9#define ATTRIB_PRINTF(start, end)
10#endif
11
6void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2); 12void _sway_abort(const char *filename, ...) ATTRIB_PRINTF(1, 2);
7#define sway_abort(FMT, ...) \ 13#define sway_abort(FMT, ...) \
8 _sway_abort("[%s:%d] " FMT, wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) 14 _sway_abort("[%s:%d] " FMT, _wlr_strip_path(__FILE__), __LINE__, ##__VA_ARGS__)
9 15
10bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3); 16bool _sway_assert(bool condition, const char* format, ...) ATTRIB_PRINTF(2, 3);
11#define sway_assert(COND, FMT, ...) \ 17#define sway_assert(COND, FMT, ...) \
12 _sway_assert(COND, "[%s:%d] %s:" FMT, wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__) 18 _sway_assert(COND, "[%s:%d] %s:" FMT, _wlr_strip_path(__FILE__), __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
13 19
14void error_handler(int sig); 20void error_handler(int sig);
15 21
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 6d17144a..1e93e2a3 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -95,7 +95,6 @@ sway_cmd cmd_client_unfocused;
95sway_cmd cmd_client_urgent; 95sway_cmd cmd_client_urgent;
96sway_cmd cmd_client_placeholder; 96sway_cmd cmd_client_placeholder;
97sway_cmd cmd_client_background; 97sway_cmd cmd_client_background;
98sway_cmd cmd_clipboard;
99sway_cmd cmd_commands; 98sway_cmd cmd_commands;
100sway_cmd cmd_debuglog; 99sway_cmd cmd_debuglog;
101sway_cmd cmd_default_border; 100sway_cmd cmd_default_border;
@@ -153,6 +152,7 @@ sway_cmd cmd_swaybg_command;
153sway_cmd cmd_swap; 152sway_cmd cmd_swap;
154sway_cmd cmd_title_format; 153sway_cmd cmd_title_format;
155sway_cmd cmd_unmark; 154sway_cmd cmd_unmark;
155sway_cmd cmd_urgent;
156sway_cmd cmd_workspace; 156sway_cmd cmd_workspace;
157sway_cmd cmd_ws_auto_back_and_forth; 157sway_cmd cmd_ws_auto_back_and_forth;
158sway_cmd cmd_workspace_layout; 158sway_cmd cmd_workspace_layout;
@@ -208,8 +208,10 @@ sway_cmd input_cmd_natural_scroll;
208sway_cmd input_cmd_pointer_accel; 208sway_cmd input_cmd_pointer_accel;
209sway_cmd input_cmd_repeat_delay; 209sway_cmd input_cmd_repeat_delay;
210sway_cmd input_cmd_repeat_rate; 210sway_cmd input_cmd_repeat_rate;
211sway_cmd input_cmd_scroll_button;
211sway_cmd input_cmd_scroll_method; 212sway_cmd input_cmd_scroll_method;
212sway_cmd input_cmd_tap; 213sway_cmd input_cmd_tap;
214sway_cmd input_cmd_tap_button_map;
213sway_cmd input_cmd_xkb_layout; 215sway_cmd input_cmd_xkb_layout;
214sway_cmd input_cmd_xkb_model; 216sway_cmd input_cmd_xkb_model;
215sway_cmd input_cmd_xkb_options; 217sway_cmd input_cmd_xkb_options;
diff --git a/include/sway/config.h b/include/sway/config.h
index ac668c24..f660a269 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -75,9 +75,11 @@ struct input_config {
75 float pointer_accel; 75 float pointer_accel;
76 int repeat_delay; 76 int repeat_delay;
77 int repeat_rate; 77 int repeat_rate;
78 int scroll_button;
78 int scroll_method; 79 int scroll_method;
79 int send_events; 80 int send_events;
80 int tap; 81 int tap;
82 int tap_button_map;
81 83
82 char *xkb_layout; 84 char *xkb_layout;
83 char *xkb_model; 85 char *xkb_model;
@@ -271,11 +273,10 @@ enum ipc_feature {
271 IPC_FEATURE_EVENT_WINDOW = 2048, 273 IPC_FEATURE_EVENT_WINDOW = 2048,
272 IPC_FEATURE_EVENT_BINDING = 4096, 274 IPC_FEATURE_EVENT_BINDING = 4096,
273 IPC_FEATURE_EVENT_INPUT = 8192, 275 IPC_FEATURE_EVENT_INPUT = 8192,
274 IPC_FEATURE_GET_CLIPBOARD = 16384, 276 IPC_FEATURE_GET_SEATS = 16384,
275 IPC_FEATURE_GET_SEATS = 32768,
276 277
277 IPC_FEATURE_ALL_COMMANDS = 278 IPC_FEATURE_ALL_COMMANDS =
278 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384 | 32768, 279 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 16384,
279 IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192, 280 IPC_FEATURE_ALL_EVENTS = 256 | 512 | 1024 | 2048 | 4096 | 8192,
280 281
281 IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS, 282 IPC_FEATURE_ALL = IPC_FEATURE_ALL_COMMANDS | IPC_FEATURE_ALL_EVENTS,
@@ -341,6 +342,7 @@ struct sway_config {
341 int gaps_outer; 342 int gaps_outer;
342 343
343 list_t *config_chain; 344 list_t *config_chain;
345 const char *current_config_path;
344 const char *current_config; 346 const char *current_config;
345 347
346 enum sway_container_border border; 348 enum sway_container_border border;
@@ -496,7 +498,4 @@ void config_update_font_height(bool recalculate);
496/* Global config singleton. */ 498/* Global config singleton. */
497extern struct sway_config *config; 499extern struct sway_config *config;
498 500
499/* Config file currently being read */
500extern const char *current_config_path;
501
502#endif 501#endif
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index bd3ca0ac..6a8337c5 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -6,9 +6,10 @@
6#include "tree/view.h" 6#include "tree/view.h"
7 7
8enum criteria_type { 8enum criteria_type {
9 CT_COMMAND = 1 << 0, 9 CT_COMMAND = 1 << 0,
10 CT_ASSIGN_OUTPUT = 1 << 1, 10 CT_ASSIGN_OUTPUT = 1 << 1,
11 CT_ASSIGN_WORKSPACE = 1 << 2, 11 CT_ASSIGN_WORKSPACE = 1 << 2,
12 CT_NO_FOCUS = 1 << 3,
12}; 13};
13 14
14struct criteria { 15struct criteria {
diff --git a/include/sway/debug.h b/include/sway/debug.h
index 2430d319..38d4eccd 100644
--- a/include/sway/debug.h
+++ b/include/sway/debug.h
@@ -1,7 +1,15 @@
1#ifndef SWAY_DEBUG_H 1#ifndef SWAY_DEBUG_H
2#define SWAY_DEBUG_H 2#define SWAY_DEBUG_H
3 3
4// Tree
4extern bool enable_debug_tree; 5extern bool enable_debug_tree;
5void update_debug_tree(); 6void update_debug_tree();
6 7
8// Damage
9extern const char *damage_debug;
10
11// Transactions
12extern int txn_timeout_ms;
13extern bool txn_debug;
14
7#endif 15#endif
diff --git a/include/sway/desktop.h b/include/sway/desktop.h
index f1ad759a..348fb187 100644
--- a/include/sway/desktop.h
+++ b/include/sway/desktop.h
@@ -1,4 +1,8 @@
1#include <wlr/types/wlr_surface.h> 1#include <wlr/types/wlr_surface.h>
2 2
3struct sway_container;
4
3void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, 5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
4 bool whole); 6 bool whole);
7
8void desktop_damage_whole_container(struct sway_container *con);
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 7ab80eb8..cee4afed 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -6,34 +6,25 @@
6/** 6/**
7 * Transactions enable us to perform atomic layout updates. 7 * Transactions enable us to perform atomic layout updates.
8 * 8 *
9 * When we want to make adjustments to the layout, we create a transaction. 9 * A transaction contains a list of containers and their new state.
10 * A transaction contains a list of affected containers and their new state.
11 * A state might contain a new size, or new border settings, or new parent/child 10 * A state might contain a new size, or new border settings, or new parent/child
12 * relationships. 11 * relationships.
13 * 12 *
14 * Calling transaction_commit() makes sway notify of all the affected clients 13 * Committing a transaction makes sway notify of all the affected clients with
15 * with their new sizes. We then wait for all the views to respond with their 14 * their new sizes. We then wait for all the views to respond with their new
16 * new surface sizes. When all are ready, or when a timeout has passed, we apply 15 * surface sizes. When all are ready, or when a timeout has passed, we apply the
17 * the updates all at the same time. 16 * updates all at the same time.
18 */ 17 *
19 18 * When we want to make adjustments to the layout, we change the pending state
20struct sway_transaction; 19 * in containers, mark them as dirty and call transaction_commit_dirty(). This
21 20 * create and commits a transaction from the dirty containers.
22/**
23 * Create a new transaction.
24 */
25struct sway_transaction *transaction_create(void);
26
27/**
28 * Add a container's pending state to the transaction.
29 */ 21 */
30void transaction_add_container(struct sway_transaction *transaction,
31 struct sway_container *container);
32 22
33/** 23/**
34 * Submit a transaction to the client views for configuration. 24 * Find all dirty containers, create and commit a transaction containing them,
25 * and unmark them as dirty.
35 */ 26 */
36void transaction_commit(struct sway_transaction *transaction); 27void transaction_commit_dirty(void);
37 28
38/** 29/**
39 * Notify the transaction system that a view is ready for the new layout. 30 * Notify the transaction system that a view is ready for the new layout.
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 0e440701..1f7792ba 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -119,17 +119,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
119 struct sway_container *container); 119 struct sway_container *container);
120 120
121/** 121/**
122 * Return the immediate child of container which was most recently focused, with
123 * fallback to selecting the child in the parent's `current` (rendered) children
124 * list.
125 *
126 * This is useful for when a tabbed container and its children are destroyed but
127 * still being rendered, and we have to render an appropriate child.
128 */
129struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
130 struct sway_container *container);
131
132/**
133 * Iterate over the focus-inactive children of the container calling the 122 * Iterate over the focus-inactive children of the container calling the
134 * function on each. 123 * function on each.
135 */ 124 */
diff --git a/include/sway/server.h b/include/sway/server.h
index a3e32898..a017d1c4 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -47,10 +47,7 @@ struct sway_server {
47 bool debug_txn_timings; 47 bool debug_txn_timings;
48 48
49 list_t *transactions; 49 list_t *transactions;
50 50 list_t *dirty_containers;
51 // When a view is being destroyed and is waiting for a transaction to
52 // complete it will be stored here.
53 list_t *destroying_containers;
54}; 51};
55 52
56struct sway_server server; 53struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index 58235642..d6abcc81 100644
--- a/include/sway/tree/arrange.h
+++ b/include/sway/tree/arrange.h
@@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c);
11void add_gaps(struct sway_container *c); 11void add_gaps(struct sway_container *c);
12 12
13/** 13/**
14 * Arrange layout for all the children of the given container, and add them to 14 * Arrange layout for all the children of the given container.
15 * the given transaction.
16 *
17 * Use this function if you need to arrange multiple sections of the tree in one
18 * transaction.
19 *
20 * You must set the desired state of the container before calling
21 * arrange_windows, then don't change any state-tracked properties in the
22 * container until you've called transaction_commit.
23 */ 15 */
24void arrange_windows(struct sway_container *container, 16void arrange_windows(struct sway_container *container);
25 struct sway_transaction *transaction);
26
27/**
28 * Arrange layout for the given container and commit the transaction.
29 *
30 * This function is a wrapper around arrange_windows, and handles creating and
31 * committing the transaction for you. Use this function if you're only doing
32 * one arrange operation.
33 */
34void arrange_and_commit(struct sway_container *container);
35 17
36#endif 18#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 728daa84..ca7a3288 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -68,6 +68,9 @@ struct sway_container_state {
68 struct sway_container *parent; 68 struct sway_container *parent;
69 list_t *children; 69 list_t *children;
70 70
71 struct sway_container *focused_inactive_child;
72 bool focused;
73
71 // View properties 74 // View properties
72 double view_x, view_y; 75 double view_x, view_y;
73 double view_width, view_height; 76 double view_width, view_height;
@@ -144,6 +147,10 @@ struct sway_container {
144 147
145 bool destroying; 148 bool destroying;
146 149
150 // If true, indicates that the container has pending state that differs from
151 // the current.
152 bool dirty;
153
147 struct { 154 struct {
148 struct wl_signal destroy; 155 struct wl_signal destroy;
149 // Raised after the tree updates, but before arrange_windows 156 // Raised after the tree updates, but before arrange_windows
@@ -297,4 +304,18 @@ bool container_is_floating(struct sway_container *container);
297 */ 304 */
298void container_get_box(struct sway_container *container, struct wlr_box *box); 305void container_get_box(struct sway_container *container, struct wlr_box *box);
299 306
307/**
308 * Move a floating container to a new layout-local position.
309 */
310void container_floating_move_to(struct sway_container *con,
311 double lx, double ly);
312
313/**
314 * Mark a container as dirty if it isn't already. Dirty containers will be
315 * included in the next transaction then unmarked as dirty.
316 */
317void container_set_dirty(struct sway_container *container);
318
319bool container_has_urgent_child(struct sway_container *container);
320
300#endif 321#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 7dc8ac46..e270f851 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -35,6 +35,7 @@ struct sway_view_impl {
35 void (*set_tiled)(struct sway_view *view, bool tiled); 35 void (*set_tiled)(struct sway_view *view, bool tiled);
36 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 36 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
37 bool (*wants_floating)(struct sway_view *view); 37 bool (*wants_floating)(struct sway_view *view);
38 bool (*has_client_side_decorations)(struct sway_view *view);
38 void (*for_each_surface)(struct sway_view *view, 39 void (*for_each_surface)(struct sway_view *view,
39 wlr_surface_iterator_func_t iterator, void *user_data); 40 wlr_surface_iterator_func_t iterator, void *user_data);
40 void (*close)(struct sway_view *view); 41 void (*close)(struct sway_view *view);
@@ -68,6 +69,11 @@ struct sway_view {
68 bool border_bottom; 69 bool border_bottom;
69 bool border_left; 70 bool border_left;
70 bool border_right; 71 bool border_right;
72 bool using_csd;
73
74 struct timespec urgent;
75 bool allow_request_urgent;
76 struct wl_event_source *urgent_timer;
71 77
72 bool destroying; 78 bool destroying;
73 79
@@ -304,4 +310,8 @@ void view_update_marks_textures(struct sway_view *view);
304 */ 310 */
305bool view_is_visible(struct sway_view *view); 311bool view_is_visible(struct sway_view *view);
306 312
313void view_set_urgent(struct sway_view *view, bool enable);
314
315bool view_is_urgent(struct sway_view *view);
316
307#endif 317#endif
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index c72a4ac0..bc95317a 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -10,6 +10,7 @@ struct sway_workspace {
10 struct sway_view *fullscreen; 10 struct sway_view *fullscreen;
11 struct sway_container *floating; 11 struct sway_container *floating;
12 list_t *output_priority; 12 list_t *output_priority;
13 bool urgent;
13}; 14};
14 15
15extern char *prev_workspace_name; 16extern char *prev_workspace_name;
@@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace,
42 43
43struct sway_container *workspace_output_get_highest_available( 44struct sway_container *workspace_output_get_highest_available(
44 struct sway_container *ws, struct sway_container *exclude); 45 struct sway_container *ws, struct sway_container *exclude);
46
47void workspace_detect_urgent(struct sway_container *workspace);
48
45#endif 49#endif
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index af478f33..f1ff25b2 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -16,11 +16,24 @@ struct swaybar_pointer {
16 int x, y; 16 int x, y;
17}; 17};
18 18
19enum x11_button {
20 NONE,
21 LEFT,
22 MIDDLE,
23 RIGHT,
24 SCROLL_UP,
25 SCROLL_DOWN,
26 SCROLL_LEFT,
27 SCROLL_RIGHT,
28 BACK,
29 FORWARD,
30};
31
19struct swaybar_hotspot { 32struct swaybar_hotspot {
20 struct wl_list link; 33 struct wl_list link;
21 int x, y, width, height; 34 int x, y, width, height;
22 void (*callback)(struct swaybar_output *output, 35 void (*callback)(struct swaybar_output *output,
23 int x, int y, uint32_t button, void *data); 36 int x, int y, enum x11_button button, void *data);
24 void (*destroy)(void *data); 37 void (*destroy)(void *data);
25 void *data; 38 void *data;
26}; 39};
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index bf12a842..2eaf8140 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -72,7 +72,9 @@ bool status_handle_readable(struct status_line *status);
72void status_line_free(struct status_line *status); 72void status_line_free(struct status_line *status);
73bool i3bar_handle_readable(struct status_line *status); 73bool i3bar_handle_readable(struct status_line *status);
74void i3bar_block_send_click(struct status_line *status, 74void i3bar_block_send_click(struct status_line *status,
75 struct i3bar_block *block, int x, int y, uint32_t button); 75 struct i3bar_block *block, int x, int y, enum x11_button button);
76void i3bar_block_free(struct i3bar_block *block); 76void i3bar_block_free(struct i3bar_block *block);
77enum x11_button wl_button_to_x11_button(uint32_t button);
78enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value);
77 79
78#endif 80#endif
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index cf80a6ba..950cfaaf 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -19,9 +19,31 @@ enum auth_state {
19 AUTH_STATE_INVALID, 19 AUTH_STATE_INVALID,
20}; 20};
21 21
22struct swaylock_colorset {
23 uint32_t input;
24 uint32_t cleared;
25 uint32_t verifying;
26 uint32_t wrong;
27};
28
29struct swaylock_colors {
30 uint32_t background;
31 uint32_t bs_highlight;
32 uint32_t key_highlight;
33 uint32_t separator;
34 struct swaylock_colorset inside;
35 struct swaylock_colorset line;
36 struct swaylock_colorset ring;
37 struct swaylock_colorset text;
38};
39
22struct swaylock_args { 40struct swaylock_args {
23 uint32_t color; 41 struct swaylock_colors colors;
24 enum background_mode mode; 42 enum background_mode mode;
43 char *font;
44 uint32_t radius;
45 uint32_t thickness;
46 bool ignore_empty;
25 bool show_indicator; 47 bool show_indicator;
26 bool daemonize; 48 bool daemonize;
27}; 49};
diff --git a/sway/commands.c b/sway/commands.c
index ef477f38..a3e6a500 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -98,8 +98,11 @@ static struct cmd_handler handlers[] = {
98 { "client.unfocused", cmd_client_unfocused }, 98 { "client.unfocused", cmd_client_unfocused },
99 { "client.urgent", cmd_client_urgent }, 99 { "client.urgent", cmd_client_urgent },
100 { "default_border", cmd_default_border }, 100 { "default_border", cmd_default_border },
101 { "default_floating_border", cmd_default_floating_border },
101 { "exec", cmd_exec }, 102 { "exec", cmd_exec },
102 { "exec_always", cmd_exec_always }, 103 { "exec_always", cmd_exec_always },
104 { "floating_maximum_size", cmd_floating_maximum_size },
105 { "floating_minimum_size", cmd_floating_minimum_size },
103 { "focus_follows_mouse", cmd_focus_follows_mouse }, 106 { "focus_follows_mouse", cmd_focus_follows_mouse },
104 { "focus_wrapping", cmd_focus_wrapping }, 107 { "focus_wrapping", cmd_focus_wrapping },
105 { "font", cmd_font }, 108 { "font", cmd_font },
@@ -112,6 +115,7 @@ static struct cmd_handler handlers[] = {
112 { "input", cmd_input }, 115 { "input", cmd_input },
113 { "mode", cmd_mode }, 116 { "mode", cmd_mode },
114 { "mouse_warping", cmd_mouse_warping }, 117 { "mouse_warping", cmd_mouse_warping },
118 { "no_focus", cmd_no_focus },
115 { "output", cmd_output }, 119 { "output", cmd_output },
116 { "seat", cmd_seat }, 120 { "seat", cmd_seat },
117 { "set", cmd_set }, 121 { "set", cmd_set },
@@ -151,6 +155,7 @@ static struct cmd_handler command_handlers[] = {
151 { "swap", cmd_swap }, 155 { "swap", cmd_swap },
152 { "title_format", cmd_title_format }, 156 { "title_format", cmd_title_format },
153 { "unmark", cmd_unmark }, 157 { "unmark", cmd_unmark },
158 { "urgent", cmd_urgent },
154}; 159};
155 160
156static int handler_compare(const void *_a, const void *_b) { 161static int handler_compare(const void *_a, const void *_b) {
@@ -163,7 +168,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
163 int handlers_size) { 168 int handlers_size) {
164 struct cmd_handler d = { .command=line }; 169 struct cmd_handler d = { .command=line };
165 struct cmd_handler *res = NULL; 170 struct cmd_handler *res = NULL;
166 wlr_log(L_DEBUG, "find_handler(%s)", line); 171 wlr_log(WLR_DEBUG, "find_handler(%s)", line);
167 172
168 bool config_loading = config->reading || !config->active; 173 bool config_loading = config->reading || !config->active;
169 174
@@ -248,10 +253,10 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
248 cmd = argsep(&cmdlist, ","); 253 cmd = argsep(&cmdlist, ",");
249 cmd += strspn(cmd, whitespace); 254 cmd += strspn(cmd, whitespace);
250 if (strcmp(cmd, "") == 0) { 255 if (strcmp(cmd, "") == 0) {
251 wlr_log(L_INFO, "Ignoring empty command."); 256 wlr_log(WLR_INFO, "Ignoring empty command.");
252 continue; 257 continue;
253 } 258 }
254 wlr_log(L_INFO, "Handling command '%s'", cmd); 259 wlr_log(WLR_INFO, "Handling command '%s'", cmd);
255 //TODO better handling of argv 260 //TODO better handling of argv
256 int argc; 261 int argc;
257 char **argv = split_args(cmd, &argc); 262 char **argv = split_args(cmd, &argc);
@@ -344,7 +349,7 @@ struct cmd_results *config_command(char *exec) {
344 349
345 // Start block 350 // Start block
346 if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { 351 if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) {
347 char *block = join_args(argv, argc - 1); 352 char *block = join_args(argv, argc - 1);
348 results = cmd_results_new(CMD_BLOCK, block, NULL); 353 results = cmd_results_new(CMD_BLOCK, block, NULL);
349 free(block); 354 free(block);
350 goto cleanup; 355 goto cleanup;
@@ -355,7 +360,7 @@ struct cmd_results *config_command(char *exec) {
355 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); 360 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
356 goto cleanup; 361 goto cleanup;
357 } 362 }
358 wlr_log(L_INFO, "handling config command '%s'", exec); 363 wlr_log(WLR_INFO, "handling config command '%s'", exec);
359 struct cmd_handler *handler = find_handler(argv[0], NULL, 0); 364 struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
360 if (!handler) { 365 if (!handler) {
361 char *input = argv[0] ? argv[0] : "(empty)"; 366 char *input = argv[0] ? argv[0] : "(empty)";
@@ -388,7 +393,7 @@ cleanup:
388struct cmd_results *config_subcommand(char **argv, int argc, 393struct cmd_results *config_subcommand(char **argv, int argc,
389 struct cmd_handler *handlers, size_t handlers_size) { 394 struct cmd_handler *handlers, size_t handlers_size) {
390 char *command = join_args(argv, argc); 395 char *command = join_args(argv, argc);
391 wlr_log(L_DEBUG, "Subcommand: %s", command); 396 wlr_log(WLR_DEBUG, "Subcommand: %s", command);
392 free(command); 397 free(command);
393 398
394 struct cmd_handler *handler = find_handler(argv[0], handlers, 399 struct cmd_handler *handler = find_handler(argv[0], handlers,
@@ -479,7 +484,7 @@ struct cmd_results *config_commands_command(char *exec) {
479 } 484 }
480 policy->context = context; 485 policy->context = context;
481 486
482 wlr_log(L_INFO, "Set command policy for %s to %d", 487 wlr_log(WLR_INFO, "Set command policy for %s to %d",
483 policy->command, policy->context); 488 policy->command, policy->context);
484 489
485 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 490 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -493,7 +498,7 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
493 const char *input, const char *format, ...) { 498 const char *input, const char *format, ...) {
494 struct cmd_results *results = malloc(sizeof(struct cmd_results)); 499 struct cmd_results *results = malloc(sizeof(struct cmd_results));
495 if (!results) { 500 if (!results) {
496 wlr_log(L_ERROR, "Unable to allocate command results"); 501 wlr_log(WLR_ERROR, "Unable to allocate command results");
497 return NULL; 502 return NULL;
498 } 503 }
499 results->status = status; 504 results->status = status;
diff --git a/sway/commands/assign.c b/sway/commands/assign.c
index a90498ce..0bc0929a 100644
--- a/sway/commands/assign.c
+++ b/sway/commands/assign.c
@@ -45,7 +45,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
45 criteria->target = join_args(argv, target_len); 45 criteria->target = join_args(argv, target_len);
46 46
47 list_add(config->criteria, criteria); 47 list_add(config->criteria, criteria);
48 wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, 48 wlr_log(WLR_DEBUG, "assign: '%s' -> '%s' added", criteria->raw,
49 criteria->target); 49 criteria->target);
50 50
51 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 51 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index d84ce808..f6a70c17 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -63,13 +63,13 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
63 for (int i = 0; i < config->bars->length; ++i) { 63 for (int i = 0; i < config->bars->length; ++i) {
64 struct bar_config *item = config->bars->items[i]; 64 struct bar_config *item = config->bars->items[i];
65 if (strcmp(item->id, argv[0]) == 0) { 65 if (strcmp(item->id, argv[0]) == 0) {
66 wlr_log(L_DEBUG, "Selecting bar: %s", argv[0]); 66 wlr_log(WLR_DEBUG, "Selecting bar: %s", argv[0]);
67 bar = item; 67 bar = item;
68 break; 68 break;
69 } 69 }
70 } 70 }
71 if (!bar) { 71 if (!bar) {
72 wlr_log(L_DEBUG, "Creating bar: %s", argv[0]); 72 wlr_log(WLR_DEBUG, "Creating bar: %s", argv[0]);
73 bar = default_bar_config(); 73 bar = default_bar_config();
74 if (!bar) { 74 if (!bar) {
75 return cmd_results_new(CMD_FAILURE, "bar", 75 return cmd_results_new(CMD_FAILURE, "bar",
@@ -108,7 +108,7 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
108 108
109 // Set current bar 109 // Set current bar
110 config->current_bar = bar; 110 config->current_bar = bar;
111 wlr_log(L_DEBUG, "Creating bar %s", bar->id); 111 wlr_log(WLR_DEBUG, "Creating bar %s", bar->id);
112 } 112 }
113 113
114 return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); 114 return config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers));
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index 3ba5f33f..0c48bee9 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
15 } 15 }
16 if (strcasecmp("yes", argv[0]) == 0) { 16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->binding_mode_indicator = true; 17 config->current_bar->binding_mode_indicator = true;
18 wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", 18 wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s",
19 config->current_bar->id); 19 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) { 20 } else if (strcasecmp("no", argv[0]) == 0) {
21 config->current_bar->binding_mode_indicator = false; 21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", 22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s",
23 config->current_bar->id); 23 config->current_bar->id);
24 } 24 }
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator", 25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
index f036cbc3..2aa4e895 100644
--- a/sway/commands/bar/font.c
+++ b/sway/commands/bar/font.c
@@ -15,7 +15,7 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
15 char *font = join_args(argv, argc); 15 char *font = join_args(argv, argc);
16 free(config->current_bar->font); 16 free(config->current_bar->font);
17 config->current_bar->font = font; 17 config->current_bar->font = font;
18 wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", 18 wlr_log(WLR_DEBUG, "Settings font '%s' for bar: %s",
19 config->current_bar->font, config->current_bar->id); 19 config->current_bar->font, config->current_bar->id);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21} 21}
diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c
index 3160caed..18258526 100644
--- a/sway/commands/bar/height.c
+++ b/sway/commands/bar/height.c
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) {
14 "Invalid height value: %s", argv[0]); 14 "Invalid height value: %s", argv[0]);
15 } 15 }
16 config->current_bar->height = height; 16 config->current_bar->height = height;
17 wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", 17 wlr_log(WLR_DEBUG, "Setting bar height to %d on bar: %s",
18 height, config->current_bar->id); 18 height, config->current_bar->id);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20} 20}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 6641f184..502ce2c4 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -27,7 +27,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
27 if (!config->reading) { 27 if (!config->reading) {
28 ipc_event_barconfig_update(bar); 28 ipc_event_barconfig_update(bar);
29 } 29 }
30 wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", 30 wlr_log(WLR_DEBUG, "Setting hidden_state: '%s' for bar: %s",
31 bar->hidden_state, bar->id); 31 bar->hidden_state, bar->id);
32 } 32 }
33 // free old mode 33 // free old mode
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
index 6ce86fef..65fa69fd 100644
--- a/sway/commands/bar/id.c
+++ b/sway/commands/bar/id.c
@@ -24,7 +24,7 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) {
24 } 24 }
25 } 25 }
26 26
27 wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); 27 wlr_log(WLR_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
28 28
29 // free old bar id 29 // free old bar id
30 free(config->current_bar->id); 30 free(config->current_bar->id);
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 34bb0a4f..28e2d77b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -28,7 +28,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode
28 if (!config->reading) { 28 if (!config->reading) {
29 ipc_event_barconfig_update(bar); 29 ipc_event_barconfig_update(bar);
30 } 30 }
31 wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); 31 wlr_log(WLR_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
32 } 32 }
33 33
34 // free old mode 34 // free old mode
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c
index 02f845e6..09025fff 100644
--- a/sway/commands/bar/modifier.c
+++ b/sway/commands/bar/modifier.c
@@ -30,7 +30,7 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
30 } 30 }
31 free_flat_list(split); 31 free_flat_list(split);
32 config->current_bar->modifier = mod; 32 config->current_bar->modifier = mod;
33 wlr_log(L_DEBUG, 33 wlr_log(WLR_DEBUG,
34 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); 34 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36} 36}
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
index f7ca0aa4..72754e05 100644
--- a/sway/commands/bar/output.c
+++ b/sway/commands/bar/output.c
@@ -42,7 +42,7 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
42 42
43 if (add_output) { 43 if (add_output) {
44 list_add(outputs, strdup(output)); 44 list_add(outputs, strdup(output));
45 wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", 45 wlr_log(WLR_DEBUG, "Adding bar: '%s' to output '%s'",
46 config->current_bar->id, output); 46 config->current_bar->id, output);
47 } 47 }
48 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 48 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c
index 480af724..857571fb 100644
--- a/sway/commands/bar/pango_markup.c
+++ b/sway/commands/bar/pango_markup.c
@@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
13 } 13 }
14 if (strcasecmp("enabled", argv[0]) == 0) { 14 if (strcasecmp("enabled", argv[0]) == 0) {
15 config->current_bar->pango_markup = true; 15 config->current_bar->pango_markup = true;
16 wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", 16 wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s",
17 config->current_bar->id); 17 config->current_bar->id);
18 } else if (strcasecmp("disabled", argv[0]) == 0) { 18 } else if (strcasecmp("disabled", argv[0]) == 0) {
19 config->current_bar->pango_markup = false; 19 config->current_bar->pango_markup = false;
20 wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", 20 wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s",
21 config->current_bar->id); 21 config->current_bar->id);
22 } else { 22 } else {
23 error = cmd_results_new(CMD_INVALID, "pango_markup", 23 error = cmd_results_new(CMD_INVALID, "pango_markup",
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index 9c580483..44bb4ae3 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -15,8 +15,9 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
15 char *valid[] = { "top", "bottom", "left", "right" }; 15 char *valid[] = { "top", "bottom", "left", "right" };
16 for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { 16 for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {
17 if (strcasecmp(valid[i], argv[0]) == 0) { 17 if (strcasecmp(valid[i], argv[0]) == 0) {
18 wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", 18 wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s",
19 argv[0], config->current_bar->id); 19 argv[0], config->current_bar->id);
20 free(config->current_bar->position);
20 config->current_bar->position = strdup(argv[0]); 21 config->current_bar->position = strdup(argv[0]);
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 } 23 }
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
index 1e08df6d..392ab730 100644
--- a/sway/commands/bar/separator_symbol.c
+++ b/sway/commands/bar/separator_symbol.c
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {
14 } 14 }
15 free(config->current_bar->separator_symbol); 15 free(config->current_bar->separator_symbol);
16 config->current_bar->separator_symbol = strdup(argv[0]); 16 config->current_bar->separator_symbol = strdup(argv[0]);
17 wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", 17 wlr_log(WLR_DEBUG, "Settings separator_symbol '%s' for bar: %s",
18 config->current_bar->separator_symbol, config->current_bar->id); 18 config->current_bar->separator_symbol, config->current_bar->id);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20} 20}
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c
index 5e199cde..6f6f81a3 100644
--- a/sway/commands/bar/status_command.c
+++ b/sway/commands/bar/status_command.c
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
14 } 14 }
15 free(config->current_bar->status_command); 15 free(config->current_bar->status_command);
16 config->current_bar->status_command = join_args(argv, argc); 16 config->current_bar->status_command = join_args(argv, argc);
17 wlr_log(L_DEBUG, "Feeding bar with status command: %s", 17 wlr_log(WLR_DEBUG, "Feeding bar with status command: %s",
18 config->current_bar->status_command); 18 config->current_bar->status_command);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20} 20}
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c
index 4f24a356..4e47d047 100644
--- a/sway/commands/bar/strip_workspace_numbers.c
+++ b/sway/commands/bar/strip_workspace_numbers.c
@@ -15,11 +15,11 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
15 } 15 }
16 if (strcasecmp("yes", argv[0]) == 0) { 16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->strip_workspace_numbers = true; 17 config->current_bar->strip_workspace_numbers = true;
18 wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", 18 wlr_log(WLR_DEBUG, "Stripping workspace numbers on bar: %s",
19 config->current_bar->id); 19 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) { 20 } else if (strcasecmp("no", argv[0]) == 0) {
21 config->current_bar->strip_workspace_numbers = false; 21 config->current_bar->strip_workspace_numbers = false;
22 wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", 22 wlr_log(WLR_DEBUG, "Enabling workspace numbers on bar: %s",
23 config->current_bar->id); 23 config->current_bar->id);
24 } else { 24 } else {
25 return cmd_results_new(CMD_INVALID, 25 return cmd_results_new(CMD_INVALID,
diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c
index 520cdd11..04e78e77 100644
--- a/sway/commands/bar/swaybar_command.c
+++ b/sway/commands/bar/swaybar_command.c
@@ -14,7 +14,7 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {
14 } 14 }
15 free(config->current_bar->swaybar_command); 15 free(config->current_bar->swaybar_command);
16 config->current_bar->swaybar_command = join_args(argv, argc); 16 config->current_bar->swaybar_command = join_args(argv, argc);
17 wlr_log(L_DEBUG, "Using custom swaybar command: %s", 17 wlr_log(WLR_DEBUG, "Using custom swaybar command: %s",
18 config->current_bar->swaybar_command); 18 config->current_bar->swaybar_command);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20} 20}
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c
index 6edc3a0d..a4079b2a 100644
--- a/sway/commands/bar/workspace_buttons.c
+++ b/sway/commands/bar/workspace_buttons.c
@@ -14,11 +14,11 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
14 } 14 }
15 if (strcasecmp("yes", argv[0]) == 0) { 15 if (strcasecmp("yes", argv[0]) == 0) {
16 config->current_bar->workspace_buttons = true; 16 config->current_bar->workspace_buttons = true;
17 wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", 17 wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s",
18 config->current_bar->id); 18 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->workspace_buttons = false; 20 config->current_bar->workspace_buttons = false;
21 wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", 21 wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s",
22 config->current_bar->id); 22 config->current_bar->id);
23 } else { 23 } else {
24 return cmd_results_new(CMD_INVALID, "workspace_buttons", 24 return cmd_results_new(CMD_INVALID, "workspace_buttons",
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c
index 7386f82c..701de00a 100644
--- a/sway/commands/bar/wrap_scroll.c
+++ b/sway/commands/bar/wrap_scroll.c
@@ -13,11 +13,11 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
13 } 13 }
14 if (strcasecmp("yes", argv[0]) == 0) { 14 if (strcasecmp("yes", argv[0]) == 0) {
15 config->current_bar->wrap_scroll = true; 15 config->current_bar->wrap_scroll = true;
16 wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", 16 wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s",
17 config->current_bar->id); 17 config->current_bar->id);
18 } else if (strcasecmp("no", argv[0]) == 0) { 18 } else if (strcasecmp("no", argv[0]) == 0) {
19 config->current_bar->wrap_scroll = false; 19 config->current_bar->wrap_scroll = false;
20 wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", 20 wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s",
21 config->current_bar->id); 21 config->current_bar->id);
22 } else { 22 } else {
23 return cmd_results_new(CMD_INVALID, 23 return cmd_results_new(CMD_INVALID,
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 821f9cd1..83e9e432 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -184,7 +184,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
184 for (int i = 0; i < mode_bindings->length; ++i) { 184 for (int i = 0; i < mode_bindings->length; ++i) {
185 struct sway_binding *config_binding = mode_bindings->items[i]; 185 struct sway_binding *config_binding = mode_bindings->items[i];
186 if (binding_key_compare(binding, config_binding)) { 186 if (binding_key_compare(binding, config_binding)) {
187 wlr_log(L_DEBUG, "overwriting old binding with command '%s'", 187 wlr_log(WLR_DEBUG, "overwriting old binding with command '%s'",
188 config_binding->command); 188 config_binding->command);
189 free_sway_binding(config_binding); 189 free_sway_binding(config_binding);
190 mode_bindings->items[i] = binding; 190 mode_bindings->items[i] = binding;
@@ -196,7 +196,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
196 list_add(mode_bindings, binding); 196 list_add(mode_bindings, binding);
197 } 197 }
198 198
199 wlr_log(L_DEBUG, "%s - Bound %s to command %s", 199 wlr_log(WLR_DEBUG, "%s - Bound %s to command %s",
200 bindtype, argv[0], binding->command); 200 bindtype, argv[0], binding->command);
201 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 201 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
202 202
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) {
42 container_set_geometry_from_floating_view(view->swayc); 42 container_set_geometry_from_floating_view(view->swayc);
43 } 43 }
44 44
45 arrange_and_commit(view->swayc); 45 arrange_windows(view->swayc);
46 46
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 47 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 48 if (seat->cursor) {
diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c
new file mode 100644
index 00000000..1bfc24af
--- /dev/null
+++ b/sway/commands/default_floating_border.c
@@ -0,0 +1,29 @@
1#include "log.h"
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/tree/container.h"
5
6struct cmd_results *cmd_default_floating_border(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "default_floating_border",
9 EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12
13 if (strcmp(argv[0], "none") == 0) {
14 config->floating_border = B_NONE;
15 } else if (strcmp(argv[0], "normal") == 0) {
16 config->floating_border = B_NORMAL;
17 } else if (strcmp(argv[0], "pixel") == 0) {
18 config->floating_border = B_PIXEL;
19 } else {
20 return cmd_results_new(CMD_INVALID, "default_floating_border",
21 "Expected 'default_floating_border <none|normal|pixel>' "
22 "or 'default_floating_border <normal|pixel> <px>'");
23 }
24 if (argc == 2) {
25 config->floating_border_thickness = atoi(argv[1]);
26 }
27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29}
diff --git a/sway/commands/exec.c b/sway/commands/exec.c
index 363d5bef..7fc54123 100644
--- a/sway/commands/exec.c
+++ b/sway/commands/exec.c
@@ -8,7 +8,7 @@ struct cmd_results *cmd_exec(int argc, char **argv) {
8 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL); 8 if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
9 if (config->reloading) { 9 if (config->reloading) {
10 char *args = join_args(argv, argc); 10 char *args = join_args(argv, argc);
11 wlr_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); 11 wlr_log(WLR_DEBUG, "Ignoring 'exec %s' due to reload", args);
12 free(args); 12 free(args);
13 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 13 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
14 } 14 }
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index 1c99de97..c7727857 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -20,7 +20,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
20 20
21 char *tmp = NULL; 21 char *tmp = NULL;
22 if (strcmp((char*)*argv, "--no-startup-id") == 0) { 22 if (strcmp((char*)*argv, "--no-startup-id") == 0) {
23 wlr_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored."); 23 wlr_log(WLR_INFO, "exec switch '--no-startup-id' not supported, ignored.");
24 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) { 24 if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
25 return error; 25 return error;
26 } 26 }
@@ -35,11 +35,11 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
35 strncpy(cmd, tmp, sizeof(cmd) - 1); 35 strncpy(cmd, tmp, sizeof(cmd) - 1);
36 cmd[sizeof(cmd) - 1] = 0; 36 cmd[sizeof(cmd) - 1] = 0;
37 free(tmp); 37 free(tmp);
38 wlr_log(L_DEBUG, "Executing %s", cmd); 38 wlr_log(WLR_DEBUG, "Executing %s", cmd);
39 39
40 int fd[2]; 40 int fd[2];
41 if (pipe(fd) != 0) { 41 if (pipe(fd) != 0) {
42 wlr_log(L_ERROR, "Unable to create pipe for fork"); 42 wlr_log(WLR_ERROR, "Unable to create pipe for fork");
43 } 43 }
44 44
45 pid_t pid, child; 45 pid_t pid, child;
@@ -73,7 +73,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
73 // cleanup child process 73 // cleanup child process
74 waitpid(pid, NULL, 0); 74 waitpid(pid, NULL, 0);
75 if (child > 0) { 75 if (child > 0) {
76 wlr_log(L_DEBUG, "Child process created with pid %d", child); 76 wlr_log(WLR_DEBUG, "Child process created with pid %d", child);
77 // TODO: add PID to active workspace 77 // TODO: add PID to active workspace
78 } else { 78 } else {
79 return cmd_results_new(CMD_FAILURE, "exec_always", 79 return cmd_results_new(CMD_FAILURE, "exec_always",
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) {
37 container_set_floating(container, wants_floating); 37 container_set_floating(container, wants_floating);
38 38
39 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 39 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
40 arrange_and_commit(workspace); 40 arrange_windows(workspace);
41 41
42 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
43} 43}
diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c
new file mode 100644
index 00000000..0af78908
--- /dev/null
+++ b/sway/commands/floating_minmax_size.c
@@ -0,0 +1,53 @@
1#include <errno.h>
2#include <math.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <string.h>
6#include <strings.h>
7#include <wlr/util/log.h>
8#include "sway/commands.h"
9#include "log.h"
10
11static const char* min_usage =
12 "Expected 'floating_minimum_size <width> x <height>'";
13
14static const char* max_usage =
15 "Expected 'floating_maximum_size <width> x <height>'";
16
17static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
18 const char *usage, int *config_width, int *config_height) {
19 struct cmd_results *error;
20 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 3))) {
21 return error;
22 }
23
24 char *err;
25 int width = (int)strtol(argv[0], &err, 10);
26 if (*err) {
27 return cmd_results_new(CMD_INVALID, cmd_name, usage);
28 }
29
30 if (strcmp(argv[1], "x") != 0) {
31 return cmd_results_new(CMD_INVALID, cmd_name, usage);
32 }
33
34 int height = (int)strtol(argv[2], &err, 10);
35 if (*err) {
36 return cmd_results_new(CMD_INVALID, cmd_name, usage);
37 }
38
39 *config_width = width;
40 *config_height = height;
41
42 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
43}
44
45struct cmd_results *cmd_floating_minimum_size(int argc, char **argv) {
46 return handle_command(argc, argv, "floating_minimum_size", min_usage,
47 &config->floating_minimum_width, &config->floating_minimum_height);
48}
49
50struct cmd_results *cmd_floating_maximum_size(int argc, char **argv) {
51 return handle_command(argc, argv, "floating_maximum_size", max_usage,
52 &config->floating_maximum_width, &config->floating_maximum_height);
53}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 74d9d535..b24d5007 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -1,10 +1,12 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/util/log.h> 2#include <wlr/util/log.h>
3#include "log.h" 3#include "log.h"
4#include "sway/commands.h"
4#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
5#include "sway/input/seat.h" 6#include "sway/input/seat.h"
7#include "sway/tree/arrange.h"
6#include "sway/tree/view.h" 8#include "sway/tree/view.h"
7#include "sway/commands.h" 9#include "sway/tree/workspace.h"
8 10
9static bool parse_movement_direction(const char *name, 11static bool parse_movement_direction(const char *name,
10 enum movement_direction *out) { 12 enum movement_direction *out) {
@@ -27,6 +29,21 @@ static bool parse_movement_direction(const char *name,
27 return true; 29 return true;
28} 30}
29 31
32static struct cmd_results *focus_mode(struct sway_container *con,
33 struct sway_seat *seat, bool floating) {
34 struct sway_container *ws = con->type == C_WORKSPACE ?
35 con : container_parent(con, C_WORKSPACE);
36 struct sway_container *new_focus = ws;
37 if (floating) {
38 new_focus = ws->sway_workspace->floating;
39 if (new_focus->children->length == 0) {
40 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
41 }
42 }
43 seat_set_focus(seat, seat_get_active_child(seat, new_focus));
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45}
46
30struct cmd_results *cmd_focus(int argc, char **argv) { 47struct cmd_results *cmd_focus(int argc, char **argv) {
31 struct sway_container *con = config->handler_context.current_container; 48 struct sway_container *con = config->handler_context.current_container;
32 struct sway_seat *seat = config->handler_context.seat; 49 struct sway_seat *seat = config->handler_context.seat;
@@ -40,11 +57,20 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
40 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 57 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
41 } 58 }
42 59
43 // TODO mode_toggle 60 if (strcmp(argv[0], "floating") == 0) {
61 return focus_mode(con, seat, true);
62 } else if (strcmp(argv[0], "tiling") == 0) {
63 return focus_mode(con, seat, false);
64 } else if (strcmp(argv[0], "mode_toggle") == 0) {
65 return focus_mode(con, seat, !container_is_floating(con));
66 }
67
68 // TODO: focus output <direction|name>
44 enum movement_direction direction = 0; 69 enum movement_direction direction = 0;
45 if (!parse_movement_direction(argv[0], &direction)) { 70 if (!parse_movement_direction(argv[0], &direction)) {
46 return cmd_results_new(CMD_INVALID, "focus", 71 return cmd_results_new(CMD_INVALID, "focus",
47 "Expected 'focus <direction|parent|child|mode_toggle>' or 'focus output <direction|name>'"); 72 "Expected 'focus <direction|parent|child|mode_toggle|floating|tiling>' "
73 "or 'focus output <direction|name>'");
48 } 74 }
49 75
50 struct sway_container *next_focus = container_get_in_direction( 76 struct sway_container *next_focus = container_get_in_direction(
diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c
index 8c425a1d..ac4d6563 100644
--- a/sway/commands/for_window.c
+++ b/sway/commands/for_window.c
@@ -24,7 +24,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
24 criteria->cmdlist = join_args(argv + 1, argc - 1); 24 criteria->cmdlist = join_args(argv + 1, argc - 1);
25 25
26 list_add(config->criteria, criteria); 26 list_add(config->criteria, criteria);
27 wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); 27 wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist);
28 28
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 30}
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) {
34 view_set_fullscreen(view, wants_fullscreen); 34 view_set_fullscreen(view, wants_fullscreen);
35 35
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_and_commit(workspace->parent); 37 arrange_windows(workspace->parent);
38 38
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 40}
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) {
43 return cmd_results_new(CMD_INVALID, "gaps", 43 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 44 "gaps edge_gaps on|off|toggle");
45 } 45 }
46 arrange_and_commit(&root_container); 46 arrange_windows(&root_container);
47 } else { 47 } else {
48 int amount_idx = 0; // the current index in argv 48 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 49 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 124 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 125 config->gaps_inner = val;
126 config->gaps_outer = val; 126 config->gaps_outer = val;
127 arrange_and_commit(&root_container); 127 arrange_windows(&root_container);
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 129 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 130 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 155 } else {
156 config->gaps_outer = total; 156 config->gaps_outer = total;
157 } 157 }
158 arrange_and_commit(&root_container); 158 arrange_windows(&root_container);
159 } else { 159 } else {
160 struct sway_container *c = 160 struct sway_container *c =
161 config->handler_context.current_container; 161 config->handler_context.current_container;
@@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
169 c->gaps_outer = total; 169 c->gaps_outer = total;
170 } 170 }
171 171
172 arrange_and_commit(c->parent ? c->parent : &root_container); 172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 173 }
174 } 174 }
175 175
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 678c57c4..5b203ea0 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -20,8 +20,10 @@ static struct cmd_handler input_handlers[] = {
20 { "pointer_accel", input_cmd_pointer_accel }, 20 { "pointer_accel", input_cmd_pointer_accel },
21 { "repeat_delay", input_cmd_repeat_delay }, 21 { "repeat_delay", input_cmd_repeat_delay },
22 { "repeat_rate", input_cmd_repeat_rate }, 22 { "repeat_rate", input_cmd_repeat_rate },
23 { "scroll_button", input_cmd_scroll_button },
23 { "scroll_method", input_cmd_scroll_method }, 24 { "scroll_method", input_cmd_scroll_method },
24 { "tap", input_cmd_tap }, 25 { "tap", input_cmd_tap },
26 { "tap_button_map", input_cmd_tap_button_map },
25 { "xkb_layout", input_cmd_xkb_layout }, 27 { "xkb_layout", input_cmd_xkb_layout },
26 { "xkb_model", input_cmd_xkb_model }, 28 { "xkb_model", input_cmd_xkb_model },
27 { "xkb_options", input_cmd_xkb_options }, 29 { "xkb_options", input_cmd_xkb_options },
@@ -35,7 +37,7 @@ struct cmd_results *cmd_input(int argc, char **argv) {
35 return error; 37 return error;
36 } 38 }
37 39
38 wlr_log(L_DEBUG, "entering input block: %s", argv[0]); 40 wlr_log(WLR_DEBUG, "entering input block: %s", argv[0]);
39 41
40 config->handler_context.input_config = new_input_config(argv[0]); 42 config->handler_context.input_config = new_input_config(argv[0]);
41 if (!config->handler_context.input_config) { 43 if (!config->handler_context.input_config) {
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index e2ccdc94..abfe3b12 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -16,7 +16,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
16 return cmd_results_new(CMD_FAILURE, "events", 16 return cmd_results_new(CMD_FAILURE, "events",
17 "No input device defined."); 17 "No input device defined.");
18 } 18 }
19 wlr_log(L_DEBUG, "events for device: %s", 19 wlr_log(WLR_DEBUG, "events for device: %s",
20 current_input_config->identifier); 20 current_input_config->identifier);
21 struct input_config *new_config = 21 struct input_config *new_config =
22 new_input_config(current_input_config->identifier); 22 new_input_config(current_input_config->identifier);
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
new file mode 100644
index 00000000..350fcca2
--- /dev/null
+++ b/sway/commands/input/scroll_button.c
@@ -0,0 +1,44 @@
1#include <string.h>
2#include <strings.h>
3#include <errno.h>
4#include "sway/config.h"
5#include "sway/commands.h"
6#include "sway/input/input-manager.h"
7
8struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 struct input_config *current_input_config =
14 config->handler_context.input_config;
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "scroll_button",
17 "No input device defined.");
18 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21
22 errno = 0;
23 char *endptr;
24 int scroll_button = strtol(*argv, &endptr, 10);
25 if (endptr == *argv && scroll_button == 0) {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier must be an integer.");
29 }
30 if (errno == ERANGE) {
31 free_input_config(new_config);
32 return cmd_results_new(CMD_INVALID, "scroll_button",
33 "Scroll button identifier out of range.");
34 }
35 if (scroll_button < 0) {
36 free_input_config(new_config);
37 return cmd_results_new(CMD_INVALID, "scroll_button",
38 "Scroll button identifier cannot be negative.");
39 }
40 new_config->scroll_button = scroll_button;
41
42 apply_input_config(new_config);
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44}
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index 7d027d5d..a8d1a10c 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -28,7 +28,7 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
28 "Expected 'tap <enabled|disabled>'"); 28 "Expected 'tap <enabled|disabled>'");
29 } 29 }
30 30
31 wlr_log(L_DEBUG, "apply-tap for device: %s", 31 wlr_log(WLR_DEBUG, "apply-tap for device: %s",
32 current_input_config->identifier); 32 current_input_config->identifier);
33 apply_input_config(new_config); 33 apply_input_config(new_config);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c
new file mode 100644
index 00000000..bdbba472
--- /dev/null
+++ b/sway/commands/input/tap_button_map.c
@@ -0,0 +1,33 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/config.h"
4#include "sway/commands.h"
5#include "sway/input/input-manager.h"
6
7struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12 struct input_config *current_input_config =
13 config->handler_context.input_config;
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "tap_button_map",
16 "No input device defined.");
17 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20
21 if (strcasecmp(argv[0], "lrm") == 0) {
22 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
23 } else if (strcasecmp(argv[0], "lmr") == 0) {
24 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
25 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "tap_button_map",
28 "Expected 'tap_button_map <lrm|lmr>'");
29 }
30
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 867e65d3..9fa5a344 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
19 19
20 new_config->xkb_layout = strdup(argv[0]); 20 new_config->xkb_layout = strdup(argv[0]);
21 21
22 wlr_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s", 22 wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s",
23 current_input_config->identifier, new_config->xkb_layout); 23 current_input_config->identifier, new_config->xkb_layout);
24 apply_input_config(new_config); 24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index e8c8e04e..0d082625 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
19 19
20 new_config->xkb_model = strdup(argv[0]); 20 new_config->xkb_model = strdup(argv[0]);
21 21
22 wlr_log(L_DEBUG, "apply-xkb_model for device: %s model: %s", 22 wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s",
23 current_input_config->identifier, new_config->xkb_model); 23 current_input_config->identifier, new_config->xkb_model);
24 apply_input_config(new_config); 24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index e9ddd6e3..3059d941 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
19 19
20 new_config->xkb_options = strdup(argv[0]); 20 new_config->xkb_options = strdup(argv[0]);
21 21
22 wlr_log(L_DEBUG, "apply-xkb_options for device: %s options: %s", 22 wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s",
23 current_input_config->identifier, new_config->xkb_options); 23 current_input_config->identifier, new_config->xkb_options);
24 apply_input_config(new_config); 24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 926d0ac1..560f088e 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
19 19
20 new_config->xkb_rules = strdup(argv[0]); 20 new_config->xkb_rules = strdup(argv[0]);
21 21
22 wlr_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s", 22 wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s",
23 current_input_config->identifier, new_config->xkb_rules); 23 current_input_config->identifier, new_config->xkb_rules);
24 apply_input_config(new_config); 24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 0e3ffd41..0aa03440 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -19,7 +19,7 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
19 19
20 new_config->xkb_variant = strdup(argv[0]); 20 new_config->xkb_variant = strdup(argv[0]);
21 21
22 wlr_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s", 22 wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s",
23 current_input_config->identifier, new_config->xkb_variant); 23 current_input_config->identifier, new_config->xkb_variant);
24 apply_input_config(new_config); 24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
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) {
49 } 49 }
50 50
51 container_notify_subtree_changed(parent); 51 container_notify_subtree_changed(parent);
52 arrange_and_commit(parent); 52 arrange_windows(parent);
53 53
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 54 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55} 55}
diff --git a/sway/commands/mode.c b/sway/commands/mode.c
index d2c14468..b460fcb5 100644
--- a/sway/commands/mode.c
+++ b/sway/commands/mode.c
@@ -65,7 +65,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
65 return error; 65 return error;
66 } 66 }
67 if ((config->reading && argc > 1) || (!config->reading && argc == 1)) { 67 if ((config->reading && argc > 1) || (!config->reading && argc == 1)) {
68 wlr_log(L_DEBUG, "Switching to mode `%s' (pango=%d)", 68 wlr_log(WLR_DEBUG, "Switching to mode `%s' (pango=%d)",
69 mode->name, mode->pango); 69 mode->name, mode->pango);
70 } 70 }
71 // Set current mode 71 // Set current mode
diff --git a/sway/commands/move.c b/sway/commands/move.c
index a4fae388..6ec050a8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,11 +1,12 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <string.h> 2#include <string.h>
3#include <strings.h> 3#include <strings.h>
4#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_output.h> 5#include <wlr/types/wlr_output.h>
5#include <wlr/types/wlr_output_layout.h> 6#include <wlr/types/wlr_output_layout.h>
6#include <wlr/util/log.h> 7#include <wlr/util/log.h>
7#include "sway/commands.h" 8#include "sway/commands.h"
8#include "sway/desktop/transaction.h" 9#include "sway/input/cursor.h"
9#include "sway/input/seat.h" 10#include "sway/input/seat.h"
10#include "sway/output.h" 11#include "sway/output.h"
11#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
@@ -103,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
103 // TODO: Ideally we would arrange the surviving parent after reaping, 104 // TODO: Ideally we would arrange the surviving parent after reaping,
104 // but container_reap_empty does not return it, so we arrange the 105 // but container_reap_empty does not return it, so we arrange the
105 // workspace instead. 106 // workspace instead.
106 struct sway_transaction *txn = transaction_create(); 107 arrange_windows(old_ws);
107 arrange_windows(old_ws, txn); 108 arrange_windows(destination->parent);
108 arrange_windows(destination->parent, txn);
109 transaction_commit(txn);
110 109
111 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 110 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
112 } else if (strcasecmp(argv[1], "to") == 0 111 } else if (strcasecmp(argv[1], "to") == 0
@@ -142,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
142 // TODO: Ideally we would arrange the surviving parent after reaping, 141 // TODO: Ideally we would arrange the surviving parent after reaping,
143 // but container_reap_empty does not return it, so we arrange the 142 // but container_reap_empty does not return it, so we arrange the
144 // workspace instead. 143 // workspace instead.
145 struct sway_transaction *txn = transaction_create(); 144 arrange_windows(old_ws);
146 arrange_windows(old_ws, txn); 145 arrange_windows(focus->parent);
147 arrange_windows(focus->parent, txn);
148 transaction_commit(txn);
149 146
150 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 147 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
151 } 148 }
@@ -175,20 +172,56 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
175 } 172 }
176 container_move_to(current, destination); 173 container_move_to(current, destination);
177 174
178 struct sway_transaction *txn = transaction_create(); 175 arrange_windows(source);
179 arrange_windows(source, txn); 176 arrange_windows(destination);
180 arrange_windows(destination, txn);
181 transaction_commit(txn);
182 177
183 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 178 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
184} 179}
185 180
186static struct cmd_results *move_in_direction(struct sway_container *container, 181static struct cmd_results *move_in_direction(struct sway_container *container,
187 enum movement_direction direction, int move_amt) { 182 enum movement_direction direction, int argc, char **argv) {
183 int move_amt = 10;
184 if (argc > 1) {
185 char *inv;
186 move_amt = (int)strtol(argv[1], &inv, 10);
187 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
188 return cmd_results_new(CMD_FAILURE, "move",
189 "Invalid distance specified");
190 }
191 }
192
188 if (container->type == C_WORKSPACE) { 193 if (container->type == C_WORKSPACE) {
189 return cmd_results_new(CMD_FAILURE, "move", 194 return cmd_results_new(CMD_FAILURE, "move",
190 "Cannot move workspaces in a direction"); 195 "Cannot move workspaces in a direction");
191 } 196 }
197 if (container_is_floating(container)) {
198 if (container->type == C_VIEW && container->sway_view->is_fullscreen) {
199 return cmd_results_new(CMD_FAILURE, "move",
200 "Cannot move fullscreen floating container");
201 }
202 double lx = container->x;
203 double ly = container->y;
204 switch (direction) {
205 case MOVE_LEFT:
206 lx -= move_amt;
207 break;
208 case MOVE_RIGHT:
209 lx += move_amt;
210 break;
211 case MOVE_UP:
212 ly -= move_amt;
213 break;
214 case MOVE_DOWN:
215 ly += move_amt;
216 break;
217 case MOVE_PARENT:
218 case MOVE_CHILD:
219 return cmd_results_new(CMD_FAILURE, "move",
220 "Cannot move floating container to parent or child");
221 }
222 container_floating_move_to(container, lx, ly);
223 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
224 }
192 // For simplicity, we'll arrange the entire workspace. The reason for this 225 // For simplicity, we'll arrange the entire workspace. The reason for this
193 // is moving the container might reap the old parent, and container_move 226 // is moving the container might reap the old parent, and container_move
194 // does not return a surviving parent. 227 // does not return a surviving parent.
@@ -198,41 +231,86 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
198 container_move(container, direction, move_amt); 231 container_move(container, direction, move_amt);
199 struct sway_container *new_ws = container_parent(container, C_WORKSPACE); 232 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
200 233
201 struct sway_transaction *txn = transaction_create(); 234 arrange_windows(old_ws);
202 arrange_windows(old_ws, txn);
203 if (new_ws != old_ws) { 235 if (new_ws != old_ws) {
204 arrange_windows(new_ws, txn); 236 arrange_windows(new_ws);
205 } 237 }
206 transaction_commit(txn);
207 238
208 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 239 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
209} 240}
210 241
242static const char* expected_position_syntax =
243 "Expected 'move [absolute] position <x> <y>' or "
244 "'move [absolute] position mouse'";
245
246static struct cmd_results *move_to_position(struct sway_container *container,
247 int argc, char **argv) {
248 if (!container_is_floating(container)) {
249 return cmd_results_new(CMD_FAILURE, "move",
250 "Only floating containers "
251 "can be moved to an absolute position");
252 }
253 if (!argc) {
254 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
255 }
256 if (strcmp(argv[0], "absolute") == 0) {
257 --argc;
258 ++argv;
259 }
260 if (!argc) {
261 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
262 }
263 if (strcmp(argv[0], "position") == 0) {
264 --argc;
265 ++argv;
266 }
267 if (!argc) {
268 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
269 }
270 if (strcmp(argv[0], "mouse") == 0) {
271 struct sway_seat *seat = config->handler_context.seat;
272 if (!seat->cursor) {
273 return cmd_results_new(CMD_FAILURE, "move", "No cursor device");
274 }
275 double lx = seat->cursor->cursor->x - container->width / 2;
276 double ly = seat->cursor->cursor->y - container->height / 2;
277 container_floating_move_to(container, lx, ly);
278 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
279 }
280 if (argc != 2) {
281 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
282 }
283 double lx, ly;
284 char *inv;
285 lx = (double)strtol(argv[0], &inv, 10);
286 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
287 return cmd_results_new(CMD_FAILURE, "move",
288 "Invalid position specified");
289 }
290 ly = (double)strtol(argv[1], &inv, 10);
291 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
292 return cmd_results_new(CMD_FAILURE, "move",
293 "Invalid position specified");
294 }
295 container_floating_move_to(container, lx, ly);
296 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
297}
298
211struct cmd_results *cmd_move(int argc, char **argv) { 299struct cmd_results *cmd_move(int argc, char **argv) {
212 struct cmd_results *error = NULL; 300 struct cmd_results *error = NULL;
213 int move_amt = 10;
214 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 301 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
215 return error; 302 return error;
216 } 303 }
217 struct sway_container *current = config->handler_context.current_container; 304 struct sway_container *current = config->handler_context.current_container;
218 305
219 if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0)) {
220 char *inv;
221 move_amt = (int)strtol(argv[1], &inv, 10);
222 if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
223 return cmd_results_new(CMD_FAILURE, "move",
224 "Invalid distance specified");
225 }
226 }
227
228 if (strcasecmp(argv[0], "left") == 0) { 306 if (strcasecmp(argv[0], "left") == 0) {
229 return move_in_direction(current, MOVE_LEFT, move_amt); 307 return move_in_direction(current, MOVE_LEFT, argc, argv);
230 } else if (strcasecmp(argv[0], "right") == 0) { 308 } else if (strcasecmp(argv[0], "right") == 0) {
231 return move_in_direction(current, MOVE_RIGHT, move_amt); 309 return move_in_direction(current, MOVE_RIGHT, argc, argv);
232 } else if (strcasecmp(argv[0], "up") == 0) { 310 } else if (strcasecmp(argv[0], "up") == 0) {
233 return move_in_direction(current, MOVE_UP, move_amt); 311 return move_in_direction(current, MOVE_UP, argc, argv);
234 } else if (strcasecmp(argv[0], "down") == 0) { 312 } else if (strcasecmp(argv[0], "down") == 0) {
235 return move_in_direction(current, MOVE_DOWN, move_amt); 313 return move_in_direction(current, MOVE_DOWN, argc, argv);
236 } else if (strcasecmp(argv[0], "container") == 0 314 } else if (strcasecmp(argv[0], "container") == 0
237 || strcasecmp(argv[0], "window") == 0) { 315 || strcasecmp(argv[0], "window") == 0) {
238 return cmd_move_container(current, argc, argv); 316 return cmd_move_container(current, argc, argv);
@@ -244,8 +322,9 @@ struct cmd_results *cmd_move(int argc, char **argv) {
244 // TODO: scratchpad 322 // TODO: scratchpad
245 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); 323 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
246 } else if (strcasecmp(argv[0], "position") == 0) { 324 } else if (strcasecmp(argv[0], "position") == 0) {
247 // TODO: floating 325 return move_to_position(current, argc, argv);
248 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); 326 } else if (strcasecmp(argv[0], "absolute") == 0) {
327 return move_to_position(current, argc, argv);
249 } else { 328 } else {
250 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 329 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
251 } 330 }
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 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/criteria.h"
5#include "list.h"
6#include "log.h"
7
8struct cmd_results *cmd_no_focus(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 char *err_str = NULL;
15 struct criteria *criteria = criteria_parse(argv[0], &err_str);
16 if (!criteria) {
17 error = cmd_results_new(CMD_INVALID, "no_focus", err_str);
18 free(err_str);
19 return error;
20 }
21
22 criteria->type = CT_NO_FOCUS;
23 list_add(config->criteria, criteria);
24
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/output.c b/sway/commands/output.c
index f955bf90..15bbd687 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -29,7 +29,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
29 29
30 struct output_config *output = new_output_config(argv[0]); 30 struct output_config *output = new_output_config(argv[0]);
31 if (!output) { 31 if (!output) {
32 wlr_log(L_ERROR, "Failed to allocate output config"); 32 wlr_log(WLR_ERROR, "Failed to allocate output config");
33 return NULL; 33 return NULL;
34 } 34 }
35 argc--; argv++; 35 argc--; argv++;
@@ -71,7 +71,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
71 list_add(config->output_configs, output); 71 list_add(config->output_configs, output);
72 } 72 }
73 73
74 wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 74 wlr_log(WLR_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
75 "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", 75 "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)",
76 output->name, output->enabled, output->width, output->height, 76 output->name, output->enabled, output->width, output->height,
77 output->refresh_rate, output->x, output->y, output->scale, 77 output->refresh_rate, output->x, output->y, output->scale,
@@ -85,7 +85,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
85 struct sway_output *sway_output; 85 struct sway_output *sway_output;
86 wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) { 86 wl_list_for_each(sway_output, &root_container.sway_root->outputs, link) {
87 output_get_identifier(identifier, sizeof(identifier), sway_output); 87 output_get_identifier(identifier, sizeof(identifier), sway_output);
88 wlr_log(L_DEBUG, "Checking identifier %s", identifier); 88 wlr_log(WLR_DEBUG, "Checking identifier %s", identifier);
89 if (all || strcmp(sway_output->wlr_output->name, output->name) == 0 89 if (all || strcmp(sway_output->wlr_output->name, output->name) == 0
90 || strcmp(identifier, output->name) == 0) { 90 || strcmp(identifier, output->name) == 0) {
91 if (!sway_output->swayc) { 91 if (!sway_output->swayc) {
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 65b5f902..4ed56c2a 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -72,7 +72,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
72 src = strdup(p.we_wordv[0]); 72 src = strdup(p.we_wordv[0]);
73 wordfree(&p); 73 wordfree(&p);
74 if (!src) { 74 if (!src) {
75 wlr_log(L_ERROR, "Failed to duplicate string"); 75 wlr_log(WLR_ERROR, "Failed to duplicate string");
76 return cmd_results_new(CMD_FAILURE, "output", 76 return cmd_results_new(CMD_FAILURE, "output",
77 "Unable to allocate resource"); 77 "Unable to allocate resource");
78 } 78 }
@@ -80,9 +80,9 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
80 if (config->reading && *src != '/') { 80 if (config->reading && *src != '/') {
81 // src file is inside configuration dir 81 // src file is inside configuration dir
82 82
83 char *conf = strdup(config->current_config); 83 char *conf = strdup(config->current_config_path);
84 if (!conf) { 84 if (!conf) {
85 wlr_log(L_ERROR, "Failed to duplicate string"); 85 wlr_log(WLR_ERROR, "Failed to duplicate string");
86 free(src); 86 free(src);
87 return cmd_results_new(CMD_FAILURE, "output", 87 return cmd_results_new(CMD_FAILURE, "output",
88 "Unable to allocate resources"); 88 "Unable to allocate resources");
@@ -94,7 +94,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
94 if (!src) { 94 if (!src) {
95 free(rel_path); 95 free(rel_path);
96 free(conf); 96 free(conf);
97 wlr_log(L_ERROR, "Unable to allocate memory"); 97 wlr_log(WLR_ERROR, "Unable to allocate memory");
98 return cmd_results_new(CMD_FAILURE, "output", 98 return cmd_results_new(CMD_FAILURE, "output",
99 "Unable to allocate resources"); 99 "Unable to allocate resources");
100 } 100 }
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 9fc213c4..cea6a94b 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -7,11 +7,11 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
7 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { 7 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
8 return error; 8 return error;
9 } 9 }
10 if (!load_main_config(config->current_config, true)) { 10 if (!load_main_config(config->current_config_path, true)) {
11 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); 11 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
12 } 12 }
13 13
14 load_swaybars(); 14 load_swaybars();
15 arrange_and_commit(&root_container); 15 arrange_windows(&root_container);
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17} 17}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 104a3392..a380ff9c 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -68,7 +68,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
68 "Workspace already exists"); 68 "Workspace already exists");
69 } 69 }
70 70
71 wlr_log(L_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); 71 wlr_log(WLR_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
72 free(workspace->name); 72 free(workspace->name);
73 workspace->name = new_name; 73 workspace->name = new_name;
74 74
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 6357343e..e657864c 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -1,4 +1,5 @@
1#include <errno.h> 1#include <errno.h>
2#include <limits.h>
2#include <math.h> 3#include <math.h>
3#include <stdbool.h> 4#include <stdbool.h>
4#include <stdlib.h> 5#include <stdlib.h>
@@ -7,6 +8,7 @@
7#include <wlr/util/log.h> 8#include <wlr/util/log.h>
8#include "sway/commands.h" 9#include "sway/commands.h"
9#include "sway/tree/arrange.h" 10#include "sway/tree/arrange.h"
11#include "sway/tree/view.h"
10#include "log.h" 12#include "log.h"
11 13
12static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 14static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@@ -21,9 +23,18 @@ enum resize_unit {
21enum resize_axis { 23enum resize_axis {
22 RESIZE_AXIS_HORIZONTAL, 24 RESIZE_AXIS_HORIZONTAL,
23 RESIZE_AXIS_VERTICAL, 25 RESIZE_AXIS_VERTICAL,
26 RESIZE_AXIS_UP,
27 RESIZE_AXIS_DOWN,
28 RESIZE_AXIS_LEFT,
29 RESIZE_AXIS_RIGHT,
24 RESIZE_AXIS_INVALID, 30 RESIZE_AXIS_INVALID,
25}; 31};
26 32
33struct resize_amount {
34 int amount;
35 enum resize_unit unit;
36};
37
27static enum resize_unit parse_resize_unit(const char *unit) { 38static enum resize_unit parse_resize_unit(const char *unit) {
28 if (strcasecmp(unit, "px") == 0) { 39 if (strcasecmp(unit, "px") == 0) {
29 return RESIZE_UNIT_PX; 40 return RESIZE_UNIT_PX;
@@ -37,6 +48,69 @@ static enum resize_unit parse_resize_unit(const char *unit) {
37 return RESIZE_UNIT_INVALID; 48 return RESIZE_UNIT_INVALID;
38} 49}
39 50
51// Parse arguments such as "10", "10px" or "10 px".
52// Returns the number of arguments consumed.
53static int parse_resize_amount(int argc, char **argv,
54 struct resize_amount *amount) {
55 char *err;
56 amount->amount = (int)strtol(argv[0], &err, 10);
57 if (*err) {
58 // e.g. 10px
59 amount->unit = parse_resize_unit(err);
60 return 1;
61 }
62 if (argc == 1) {
63 amount->unit = RESIZE_UNIT_DEFAULT;
64 return 1;
65 }
66 // Try the second argument
67 amount->unit = parse_resize_unit(argv[1]);
68 if (amount->unit == RESIZE_UNIT_INVALID) {
69 amount->unit = RESIZE_UNIT_DEFAULT;
70 return 1;
71 }
72 return 2;
73}
74
75static void calculate_constraints(int *min_width, int *max_width,
76 int *min_height, int *max_height) {
77 struct sway_container *con = config->handler_context.current_container;
78
79 if (config->floating_minimum_width == -1) { // no minimum
80 *min_width = 0;
81 } else if (config->floating_minimum_width == 0) { // automatic
82 *min_width = 75;
83 } else {
84 *min_width = config->floating_minimum_width;
85 }
86
87 if (config->floating_minimum_height == -1) { // no minimum
88 *min_height = 0;
89 } else if (config->floating_minimum_height == 0) { // automatic
90 *min_height = 50;
91 } else {
92 *min_height = config->floating_minimum_height;
93 }
94
95 if (config->floating_maximum_width == -1) { // no maximum
96 *max_width = INT_MAX;
97 } else if (config->floating_maximum_width == 0) { // automatic
98 struct sway_container *ws = container_parent(con, C_WORKSPACE);
99 *max_width = ws->width;
100 } else {
101 *max_width = config->floating_maximum_width;
102 }
103
104 if (config->floating_maximum_height == -1) { // no maximum
105 *max_height = INT_MAX;
106 } else if (config->floating_maximum_height == 0) { // automatic
107 struct sway_container *ws = container_parent(con, C_WORKSPACE);
108 *max_height = ws->height;
109 } else {
110 *max_height = config->floating_maximum_height;
111 }
112}
113
40static enum resize_axis parse_resize_axis(const char *axis) { 114static enum resize_axis parse_resize_axis(const char *axis) {
41 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { 115 if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) {
42 return RESIZE_AXIS_HORIZONTAL; 116 return RESIZE_AXIS_HORIZONTAL;
@@ -44,6 +118,18 @@ static enum resize_axis parse_resize_axis(const char *axis) {
44 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) { 118 if (strcasecmp(axis, "height") == 0 || strcasecmp(axis, "vertical") == 0) {
45 return RESIZE_AXIS_VERTICAL; 119 return RESIZE_AXIS_VERTICAL;
46 } 120 }
121 if (strcasecmp(axis, "up") == 0) {
122 return RESIZE_AXIS_UP;
123 }
124 if (strcasecmp(axis, "down") == 0) {
125 return RESIZE_AXIS_DOWN;
126 }
127 if (strcasecmp(axis, "left") == 0) {
128 return RESIZE_AXIS_LEFT;
129 }
130 if (strcasecmp(axis, "right") == 0) {
131 return RESIZE_AXIS_RIGHT;
132 }
47 return RESIZE_AXIS_INVALID; 133 return RESIZE_AXIS_INVALID;
48} 134}
49 135
@@ -95,7 +181,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
95 return; 181 return;
96 } 182 }
97 183
98 wlr_log(L_DEBUG, 184 wlr_log(WLR_DEBUG,
99 "Found the proper parent: %p. It has %d l conts, and %d r conts", 185 "Found the proper parent: %p. It has %d l conts, and %d r conts",
100 parent->parent, minor_weight, major_weight); 186 parent->parent, minor_weight, major_weight);
101 187
@@ -182,105 +268,315 @@ static void resize_tiled(int amount, enum resize_axis axis) {
182 } 268 }
183 } 269 }
184 270
185 arrange_and_commit(parent->parent); 271 arrange_windows(parent->parent);
186} 272}
187 273
188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { 274/**
189 struct sway_container *current = config->handler_context.current_container; 275 * Implement `resize <grow|shrink>` for a floating container.
190 if (unit == RESIZE_UNIT_DEFAULT) { 276 */
191 // Default for tiling; TODO floating should be px 277static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
192 unit = RESIZE_UNIT_PPT; 278 struct resize_amount *amount) {
279 struct sway_container *con = config->handler_context.current_container;
280 int grow_width = 0, grow_height = 0;
281 switch (axis) {
282 case RESIZE_AXIS_HORIZONTAL:
283 case RESIZE_AXIS_LEFT:
284 case RESIZE_AXIS_RIGHT:
285 grow_width = amount->amount;
286 break;
287 case RESIZE_AXIS_VERTICAL:
288 case RESIZE_AXIS_UP:
289 case RESIZE_AXIS_DOWN:
290 grow_height = amount->amount;
291 break;
292 case RESIZE_AXIS_INVALID:
293 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
294 }
295 // Make sure we're not adjusting beyond floating min/max size
296 int min_width, max_width, min_height, max_height;
297 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
298 if (con->width + grow_width < min_width) {
299 grow_width = min_width - con->width;
300 } else if (con->width + grow_width > max_width) {
301 grow_width = max_width - con->width;
193 } 302 }
303 if (con->height + grow_height < min_height) {
304 grow_height = min_height - con->height;
305 } else if (con->height + grow_height > max_height) {
306 grow_height = max_height - con->height;
307 }
308 int grow_x = 0, grow_y = 0;
309 switch (axis) {
310 case RESIZE_AXIS_HORIZONTAL:
311 grow_x = -grow_width / 2;
312 break;
313 case RESIZE_AXIS_VERTICAL:
314 grow_y = -grow_height / 2;
315 break;
316 case RESIZE_AXIS_UP:
317 grow_y = -grow_height;
318 break;
319 case RESIZE_AXIS_LEFT:
320 grow_x = -grow_width;
321 break;
322 case RESIZE_AXIS_DOWN:
323 case RESIZE_AXIS_RIGHT:
324 break;
325 case RESIZE_AXIS_INVALID:
326 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
327 }
328 con->x += grow_x;
329 con->y += grow_y;
330 con->width += grow_width;
331 con->height += grow_height;
332
333 if (con->type == C_VIEW) {
334 struct sway_view *view = con->sway_view;
335 view->x += grow_x;
336 view->y += grow_y;
337 view->width += grow_width;
338 view->height += grow_height;
339 }
340
341 arrange_windows(con);
342
343 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
344}
194 345
195 if (unit == RESIZE_UNIT_PPT) { 346/**
196 float pct = amount / 100.0f; 347 * Implement `resize <grow|shrink>` for a tiled container.
348 */
349static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
350 struct resize_amount *amount) {
351 struct sway_container *current = config->handler_context.current_container;
352
353 if (amount->unit == RESIZE_UNIT_DEFAULT) {
354 amount->unit = RESIZE_UNIT_PPT;
355 }
356 if (amount->unit == RESIZE_UNIT_PPT) {
357 float pct = amount->amount / 100.0f;
358 // TODO: Make left/right/up/down resize in that direction?
197 switch (axis) { 359 switch (axis) {
360 case RESIZE_AXIS_LEFT:
361 case RESIZE_AXIS_RIGHT:
198 case RESIZE_AXIS_HORIZONTAL: 362 case RESIZE_AXIS_HORIZONTAL:
199 amount = (float)current->width * pct; 363 amount->amount = (float)current->width * pct;
200 break; 364 break;
365 case RESIZE_AXIS_UP:
366 case RESIZE_AXIS_DOWN:
201 case RESIZE_AXIS_VERTICAL: 367 case RESIZE_AXIS_VERTICAL:
202 amount = (float)current->height * pct; 368 amount->amount = (float)current->height * pct;
203 break; 369 break;
204 default: 370 case RESIZE_AXIS_INVALID:
205 sway_assert(0, "invalid resize axis"); 371 return cmd_results_new(CMD_INVALID, "resize",
206 return; 372 "Invalid resize axis/direction");
207 } 373 }
208 } 374 }
209 375
210 return resize_tiled(amount, axis); 376 resize_tiled(amount->amount, axis);
377 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
211} 378}
212 379
213struct cmd_results *cmd_resize(int argc, char **argv) { 380/**
214 struct sway_container *current = config->handler_context.current_container; 381 * Implement `resize set` for a tiled container.
215 if (!current) { 382 */
216 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 383static struct cmd_results *resize_set_tiled(struct sway_container *con,
217 } 384 struct resize_amount *width, struct resize_amount *height) {
218 if (current->type != C_VIEW && current->type != C_CONTAINER) { 385 return cmd_results_new(CMD_INVALID, "resize",
219 return cmd_results_new(CMD_INVALID, "resize", 386 "'resize set' is not implemented for tiled views");
220 "Can only resize views/containers"); 387}
388
389/**
390 * Implement `resize set` for a floating container.
391 */
392static struct cmd_results *resize_set_floating(struct sway_container *con,
393 struct resize_amount *width, struct resize_amount *height) {
394 int min_width, max_width, min_height, max_height;
395 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
396 width->amount = fmax(min_width, fmin(width->amount, max_width));
397 height->amount = fmax(min_height, fmin(height->amount, max_height));
398 int grow_width = width->amount - con->width;
399 int grow_height = height->amount - con->height;
400 con->x -= grow_width / 2;
401 con->y -= grow_height / 2;
402 con->width = width->amount;
403 con->height = height->amount;
404
405 if (con->type == C_VIEW) {
406 struct sway_view *view = con->sway_view;
407 view->x -= grow_width / 2;
408 view->y -= grow_height / 2;
409 view->width += grow_width;
410 view->height += grow_height;
221 } 411 }
222 412
413 arrange_windows(con);
414
415 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
416}
417
418/**
419 * resize set <args>
420 *
421 * args: <width> [px|ppt] <height> [px|ppt]
422 */
423static struct cmd_results *cmd_resize_set(int argc, char **argv) {
223 struct cmd_results *error; 424 struct cmd_results *error;
224 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 425 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
225 return error; 426 return error;
226 } 427 }
227 428 const char *usage = "Expected 'resize set <width> <height>'";
228 if (strcasecmp(argv[0], "set") == 0) { 429
229 // TODO 430 // Width
230 //return cmd_resize_set(argc - 1, &argv[1]); 431 struct resize_amount width;
231 return cmd_results_new(CMD_INVALID, "resize", "resize set unimplemented"); 432 int num_consumed_args = parse_resize_amount(argc, argv, &width);
433 argc -= num_consumed_args;
434 argv += num_consumed_args;
435 if (width.unit == RESIZE_UNIT_INVALID) {
436 return cmd_results_new(CMD_INVALID, "resize", usage);
437 }
438 if (!argc) {
439 return cmd_results_new(CMD_INVALID, "resize", usage);
232 } 440 }
233 441
234 // TODO: resize grow|shrink left|right|up|down 442 // Height
443 struct resize_amount height;
444 num_consumed_args = parse_resize_amount(argc, argv, &height);
445 argc -= num_consumed_args;
446 argv += num_consumed_args;
447 if (height.unit == RESIZE_UNIT_INVALID) {
448 return cmd_results_new(CMD_INVALID, "resize", usage);
449 }
235 450
236 const char *usage = "Expected 'resize <shrink|grow> " 451 // If 0, don't resize that dimension
237 "<width|height> [<amount>] [px|ppt]'"; 452 struct sway_container *con = config->handler_context.current_container;
453 if (width.amount <= 0) {
454 width.amount = con->width;
455 }
456 if (height.amount <= 0) {
457 height.amount = con->height;
458 }
238 459
239 int multiplier = 0; 460 if (container_is_floating(con)) {
240 if (strcasecmp(*argv, "grow") == 0) { 461 return resize_set_floating(con, &width, &height);
241 multiplier = 1;
242 } else if (strcasecmp(*argv, "shrink") == 0) {
243 multiplier = -1;
244 } else {
245 return cmd_results_new(CMD_INVALID, "resize", usage);
246 } 462 }
247 --argc; ++argv; 463 return resize_set_tiled(con, &width, &height);
464}
248 465
466/**
467 * resize <grow|shrink> <args>
468 *
469 * args: <direction>
470 * args: <direction> <amount> <unit>
471 * args: <direction> <amount> <unit> or <amount> <other_unit>
472 */
473static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
474 int multiplier) {
475 const char *usage = "Expected 'resize grow|shrink <direction> "
476 "[<amount> px|ppt [or <amount> px|ppt]]'";
249 enum resize_axis axis = parse_resize_axis(*argv); 477 enum resize_axis axis = parse_resize_axis(*argv);
250 if (axis == RESIZE_AXIS_INVALID) { 478 if (axis == RESIZE_AXIS_INVALID) {
251 return cmd_results_new(CMD_INVALID, "resize", usage); 479 return cmd_results_new(CMD_INVALID, "resize", usage);
252 } 480 }
253 --argc; ++argv; 481 --argc; ++argv;
254 482
255 int amount = 10; // Default amount 483 // First amount
256 enum resize_unit unit = RESIZE_UNIT_DEFAULT; 484 struct resize_amount first_amount;
257
258 if (argc) { 485 if (argc) {
259 char *err; 486 int num_consumed_args = parse_resize_amount(argc, argv, &first_amount);
260 amount = (int)strtol(*argv, &err, 10); 487 argc -= num_consumed_args;
261 if (*err) { 488 argv += num_consumed_args;
262 // e.g. `resize grow width 10px` 489 if (first_amount.unit == RESIZE_UNIT_INVALID) {
263 unit = parse_resize_unit(err); 490 return cmd_results_new(CMD_INVALID, "resize", usage);
264 if (unit == RESIZE_UNIT_INVALID) {
265 return cmd_results_new(CMD_INVALID, "resize", usage);
266 }
267 } 491 }
268 --argc; ++argv; 492 } else {
493 first_amount.amount = 10;
494 first_amount.unit = RESIZE_UNIT_DEFAULT;
269 } 495 }
270 496
497 // "or"
271 if (argc) { 498 if (argc) {
272 unit = parse_resize_unit(*argv); 499 if (strcmp(*argv, "or") != 0) {
273 if (unit == RESIZE_UNIT_INVALID) {
274 return cmd_results_new(CMD_INVALID, "resize", usage); 500 return cmd_results_new(CMD_INVALID, "resize", usage);
275 } 501 }
276 --argc; ++argv; 502 --argc; ++argv;
277 } 503 }
278 504
505 // Second amount
506 struct resize_amount second_amount;
279 if (argc) { 507 if (argc) {
280 // Provied too many args, the bastard 508 int num_consumed_args = parse_resize_amount(argc, argv, &second_amount);
281 return cmd_results_new(CMD_INVALID, "resize", usage); 509 argc -= num_consumed_args;
510 argv += num_consumed_args;
511 if (second_amount.unit == RESIZE_UNIT_INVALID) {
512 return cmd_results_new(CMD_INVALID, "resize", usage);
513 }
514 } else {
515 second_amount.unit = RESIZE_UNIT_INVALID;
282 } 516 }
283 517
284 resize(amount * multiplier, axis, unit); 518 first_amount.amount *= multiplier;
285 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 519 second_amount.amount *= multiplier;
520
521 struct sway_container *con = config->handler_context.current_container;
522 if (container_is_floating(con)) {
523 // Floating containers can only resize in px. Choose an amount which
524 // uses px, with fallback to an amount that specified no unit.
525 if (first_amount.unit == RESIZE_UNIT_PX) {
526 return resize_adjust_floating(axis, &first_amount);
527 } else if (second_amount.unit == RESIZE_UNIT_PX) {
528 return resize_adjust_floating(axis, &second_amount);
529 } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) {
530 return resize_adjust_floating(axis, &first_amount);
531 } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) {
532 return resize_adjust_floating(axis, &second_amount);
533 } else {
534 return cmd_results_new(CMD_INVALID, "resize",
535 "Floating containers cannot use ppt measurements");
536 }
537 }
538
539 // For tiling, prefer ppt -> default -> px
540 if (first_amount.unit == RESIZE_UNIT_PPT) {
541 return resize_adjust_tiled(axis, &first_amount);
542 } else if (second_amount.unit == RESIZE_UNIT_PPT) {
543 return resize_adjust_tiled(axis, &second_amount);
544 } else if (first_amount.unit == RESIZE_UNIT_DEFAULT) {
545 return resize_adjust_tiled(axis, &first_amount);
546 } else if (second_amount.unit == RESIZE_UNIT_DEFAULT) {
547 return resize_adjust_tiled(axis, &second_amount);
548 } else {
549 return resize_adjust_tiled(axis, &first_amount);
550 }
551}
552
553struct cmd_results *cmd_resize(int argc, char **argv) {
554 struct sway_container *current = config->handler_context.current_container;
555 if (!current) {
556 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
557 }
558 if (current->type != C_VIEW && current->type != C_CONTAINER) {
559 return cmd_results_new(CMD_INVALID, "resize",
560 "Can only resize views/containers");
561 }
562
563 struct cmd_results *error;
564 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
565 return error;
566 }
567
568 if (strcasecmp(argv[0], "set") == 0) {
569 return cmd_resize_set(argc - 1, &argv[1]);
570 }
571 if (strcasecmp(argv[0], "grow") == 0) {
572 return cmd_resize_adjust(argc - 1, &argv[1], 1);
573 }
574 if (strcasecmp(argv[0], "shrink") == 0) {
575 return cmd_resize_adjust(argc - 1, &argv[1], -1);
576 }
577
578 const char *usage = "Expected 'resize <shrink|grow> "
579 "<width|height|up|down|left|right> [<amount>] [px|ppt]'";
580
581 return cmd_results_new(CMD_INVALID, "resize", usage);
286} 582}
diff --git a/sway/commands/set.c b/sway/commands/set.c
index 84e9b792..ea388d3b 100644
--- a/sway/commands/set.c
+++ b/sway/commands/set.c
@@ -32,7 +32,7 @@ struct cmd_results *cmd_set(int argc, char **argv) {
32 } 32 }
33 33
34 if (argv[0][0] != '$') { 34 if (argv[0][0] != '$') {
35 wlr_log(L_INFO, "Warning: variable '%s' doesn't start with $", argv[0]); 35 wlr_log(WLR_INFO, "Warning: variable '%s' doesn't start with $", argv[0]);
36 36
37 size_t size = snprintf(NULL, 0, "$%s", argv[0]); 37 size_t size = snprintf(NULL, 0, "$%s", argv[0]);
38 tmp = malloc(size + 1); 38 tmp = malloc(size + 1);
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) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_and_commit(&root_container); 26 arrange_windows(&root_container);
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
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) {
16 } 16 }
17 struct sway_container *parent = container_split(con, layout); 17 struct sway_container *parent = container_split(con, layout);
18 container_create_notify(parent); 18 container_create_notify(parent);
19 arrange_and_commit(parent->parent); 19 arrange_windows(parent->parent);
20 20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22} 22}
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 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/util/log.h> 2#include <wlr/util/log.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/desktop/transaction.h"
5#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
6#include "sway/tree/layout.h" 5#include "sway/tree/layout.h"
7#include "sway/tree/view.h" 6#include "sway/tree/view.h"
@@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
79 78
80 container_swap(current, other); 79 container_swap(current, other);
81 80
82 struct sway_transaction *txn = transaction_create(); 81 arrange_windows(current->parent);
83 arrange_windows(current->parent, txn);
84
85 if (other->parent != current->parent) { 82 if (other->parent != current->parent) {
86 arrange_windows(other->parent, txn); 83 arrange_windows(other->parent);
87 } 84 }
88 85
89 transaction_commit(txn);
90
91 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 86 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
92} 87}
diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c
index 770d4821..36f7fdcd 100644
--- a/sway/commands/swaybg_command.c
+++ b/sway/commands/swaybg_command.c
@@ -13,7 +13,7 @@ struct cmd_results *cmd_swaybg_command(int argc, char **argv) {
13 free(config->swaybg_command); 13 free(config->swaybg_command);
14 } 14 }
15 config->swaybg_command = join_args(argv, argc); 15 config->swaybg_command = join_args(argv, argc);
16 wlr_log(L_DEBUG, "Using custom swaybg command: %s", 16 wlr_log(WLR_DEBUG, "Using custom swaybg command: %s",
17 config->swaybg_command); 17 config->swaybg_command);
18 18
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 19 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 @@
1#include "log.h"
2#include "sway/commands.h"
3#include "sway/config.h"
4#include "sway/tree/arrange.h"
5#include "sway/tree/container.h"
6#include "sway/tree/view.h"
7#include "sway/tree/layout.h"
8
9struct cmd_results *cmd_urgent(int argc, char **argv) {
10 struct cmd_results *error = NULL;
11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
12 return error;
13 }
14 struct sway_container *container =
15 config->handler_context.current_container;
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "urgent",
18 "Only views can be urgent");
19 }
20 struct sway_view *view = container->sway_view;
21
22 if (strcmp(argv[0], "enable") == 0) {
23 view_set_urgent(view, true);
24 } else if (strcmp(argv[0], "disable") == 0) {
25 view_set_urgent(view, false);
26 } else if (strcmp(argv[0], "allow") == 0) {
27 view->allow_request_urgent = true;
28 } else if (strcmp(argv[0], "deny") == 0) {
29 view->allow_request_urgent = false;
30 } else {
31 return cmd_results_new(CMD_INVALID, "urgent",
32 "Expected 'urgent <enable|disable|allow|deny>'");
33 }
34
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36}
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index d15be571..e8b37182 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -51,7 +51,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
51 free(old); // workspaces can only be assigned to a single output 51 free(old); // workspaces can only be assigned to a single output
52 list_del(config->workspace_outputs, i); 52 list_del(config->workspace_outputs, i);
53 } 53 }
54 wlr_log(L_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); 54 wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
55 list_add(config->workspace_outputs, wso); 55 list_add(config->workspace_outputs, wso);
56 } else { 56 } else {
57 if (config->reading || !config->active) { 57 if (config->reading || !config->active) {
diff --git a/sway/config.c b/sway/config.c
index 89b7d349..c620e4c7 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -24,6 +24,7 @@
24#include "sway/input/seat.h" 24#include "sway/input/seat.h"
25#include "sway/commands.h" 25#include "sway/commands.h"
26#include "sway/config.h" 26#include "sway/config.h"
27#include "sway/criteria.h"
27#include "sway/tree/arrange.h" 28#include "sway/tree/arrange.h"
28#include "sway/tree/layout.h" 29#include "sway/tree/layout.h"
29#include "sway/tree/workspace.h" 30#include "sway/tree/workspace.h"
@@ -105,7 +106,12 @@ void free_config(struct sway_config *config) {
105 } 106 }
106 list_free(config->seat_configs); 107 list_free(config->seat_configs);
107 } 108 }
108 list_free(config->criteria); 109 if (config->criteria) {
110 for (int i = 0; i < config->criteria->length; ++i) {
111 criteria_destroy(config->criteria->items[i]);
112 }
113 list_free(config->criteria);
114 }
109 list_free(config->no_focus); 115 list_free(config->no_focus);
110 list_free(config->active_bar_modifiers); 116 list_free(config->active_bar_modifiers);
111 list_free(config->config_chain); 117 list_free(config->config_chain);
@@ -117,6 +123,7 @@ void free_config(struct sway_config *config) {
117 free(config->floating_scroll_left_cmd); 123 free(config->floating_scroll_left_cmd);
118 free(config->floating_scroll_right_cmd); 124 free(config->floating_scroll_right_cmd);
119 free(config->font); 125 free(config->font);
126 free((char *)config->current_config_path);
120 free((char *)config->current_config); 127 free((char *)config->current_config);
121 free(config); 128 free(config);
122} 129}
@@ -205,6 +212,7 @@ static void config_defaults(struct sway_config *config) {
205 if (!(config->active_bar_modifiers = create_list())) goto cleanup; 212 if (!(config->active_bar_modifiers = create_list())) goto cleanup;
206 213
207 if (!(config->config_chain = create_list())) goto cleanup; 214 if (!(config->config_chain = create_list())) goto cleanup;
215 config->current_config_path = NULL;
208 config->current_config = NULL; 216 config->current_config = NULL;
209 217
210 // borders 218 // borders
@@ -276,12 +284,12 @@ static char *get_config_path(void) {
276 char *home = getenv("HOME"); 284 char *home = getenv("HOME");
277 char *config_home = malloc(strlen(home) + strlen("/.config") + 1); 285 char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
278 if (!config_home) { 286 if (!config_home) {
279 wlr_log(L_ERROR, "Unable to allocate $HOME/.config"); 287 wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");
280 } else { 288 } else {
281 strcpy(config_home, home); 289 strcpy(config_home, home);
282 strcat(config_home, "/.config"); 290 strcat(config_home, "/.config");
283 setenv("XDG_CONFIG_HOME", config_home, 1); 291 setenv("XDG_CONFIG_HOME", config_home, 1);
284 wlr_log(L_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); 292 wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
285 free(config_home); 293 free(config_home);
286 } 294 }
287 } 295 }
@@ -304,16 +312,13 @@ static char *get_config_path(void) {
304 return NULL; // Not reached 312 return NULL; // Not reached
305} 313}
306 314
307const char *current_config_path;
308
309static bool load_config(const char *path, struct sway_config *config) { 315static bool load_config(const char *path, struct sway_config *config) {
310 if (path == NULL) { 316 if (path == NULL) {
311 wlr_log(L_ERROR, "Unable to find a config file!"); 317 wlr_log(WLR_ERROR, "Unable to find a config file!");
312 return false; 318 return false;
313 } 319 }
314 320
315 wlr_log(L_INFO, "Loading config from %s", path); 321 wlr_log(WLR_INFO, "Loading config from %s", path);
316 current_config_path = path;
317 322
318 struct stat sb; 323 struct stat sb;
319 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { 324 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
@@ -322,7 +327,7 @@ static bool load_config(const char *path, struct sway_config *config) {
322 327
323 FILE *f = fopen(path, "r"); 328 FILE *f = fopen(path, "r");
324 if (!f) { 329 if (!f) {
325 wlr_log(L_ERROR, "Unable to open %s for reading", path); 330 wlr_log(WLR_ERROR, "Unable to open %s for reading", path);
326 return false; 331 return false;
327 } 332 }
328 333
@@ -330,10 +335,9 @@ static bool load_config(const char *path, struct sway_config *config) {
330 fclose(f); 335 fclose(f);
331 336
332 if (!config_load_success) { 337 if (!config_load_success) {
333 wlr_log(L_ERROR, "Error(s) loading config!"); 338 wlr_log(WLR_ERROR, "Error(s) loading config!");
334 } 339 }
335 340
336 current_config_path = NULL;
337 return true; 341 return true;
338} 342}
339 343
@@ -353,12 +357,12 @@ bool load_main_config(const char *file, bool is_active) {
353 357
354 config_defaults(config); 358 config_defaults(config);
355 if (is_active) { 359 if (is_active) {
356 wlr_log(L_DEBUG, "Performing configuration file reload"); 360 wlr_log(WLR_DEBUG, "Performing configuration file reload");
357 config->reloading = true; 361 config->reloading = true;
358 config->active = true; 362 config->active = true;
359 } 363 }
360 364
361 config->current_config = path; 365 config->current_config_path = path;
362 list_add(config->config_chain, path); 366 list_add(config->config_chain, path);
363 367
364 config->reading = true; 368 config->reading = true;
@@ -369,7 +373,7 @@ bool load_main_config(const char *file, bool is_active) {
369 /* 373 /*
370 DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); 374 DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
371 if (!dir) { 375 if (!dir) {
372 wlr_log(L_ERROR, 376 wlr_log(WLR_ERROR,
373 "%s does not exist, sway will have no security configuration" 377 "%s does not exist, sway will have no security configuration"
374 " and will probably be broken", SYSCONFDIR "/sway/security.d"); 378 " and will probably be broken", SYSCONFDIR "/sway/security.d");
375 } else { 379 } else {
@@ -398,7 +402,7 @@ bool load_main_config(const char *file, bool is_active) {
398 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || 402 if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
399 (((s.st_mode & 0777) != 0644) && 403 (((s.st_mode & 0777) != 0644) &&
400 (s.st_mode & 0777) != 0444)) { 404 (s.st_mode & 0777) != 0444)) {
401 wlr_log(L_ERROR, 405 wlr_log(WLR_ERROR,
402 "Refusing to load %s - it must be owned by root " 406 "Refusing to load %s - it must be owned by root "
403 "and mode 644 or 444", _path); 407 "and mode 644 or 444", _path);
404 success = false; 408 success = false;
@@ -428,7 +432,7 @@ bool load_main_config(const char *file, bool is_active) {
428static bool load_include_config(const char *path, const char *parent_dir, 432static bool load_include_config(const char *path, const char *parent_dir,
429 struct sway_config *config) { 433 struct sway_config *config) {
430 // save parent config 434 // save parent config
431 const char *parent_config = config->current_config; 435 const char *parent_config = config->current_config_path;
432 436
433 char *full_path; 437 char *full_path;
434 int len = strlen(path); 438 int len = strlen(path);
@@ -436,7 +440,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
436 len = len + strlen(parent_dir) + 2; 440 len = len + strlen(parent_dir) + 2;
437 full_path = malloc(len * sizeof(char)); 441 full_path = malloc(len * sizeof(char));
438 if (!full_path) { 442 if (!full_path) {
439 wlr_log(L_ERROR, 443 wlr_log(WLR_ERROR,
440 "Unable to allocate full path to included config"); 444 "Unable to allocate full path to included config");
441 return false; 445 return false;
442 } 446 }
@@ -449,7 +453,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
449 free(full_path); 453 free(full_path);
450 454
451 if (real_path == NULL) { 455 if (real_path == NULL) {
452 wlr_log(L_DEBUG, "%s not found.", path); 456 wlr_log(WLR_DEBUG, "%s not found.", path);
453 return false; 457 return false;
454 } 458 }
455 459
@@ -458,7 +462,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
458 for (j = 0; j < config->config_chain->length; ++j) { 462 for (j = 0; j < config->config_chain->length; ++j) {
459 char *old_path = config->config_chain->items[j]; 463 char *old_path = config->config_chain->items[j];
460 if (strcmp(real_path, old_path) == 0) { 464 if (strcmp(real_path, old_path) == 0) {
461 wlr_log(L_DEBUG, 465 wlr_log(WLR_DEBUG,
462 "%s already included once, won't be included again.", 466 "%s already included once, won't be included again.",
463 real_path); 467 real_path);
464 free(real_path); 468 free(real_path);
@@ -466,25 +470,25 @@ static bool load_include_config(const char *path, const char *parent_dir,
466 } 470 }
467 } 471 }
468 472
469 config->current_config = real_path; 473 config->current_config_path = real_path;
470 list_add(config->config_chain, real_path); 474 list_add(config->config_chain, real_path);
471 int index = config->config_chain->length - 1; 475 int index = config->config_chain->length - 1;
472 476
473 if (!load_config(real_path, config)) { 477 if (!load_config(real_path, config)) {
474 free(real_path); 478 free(real_path);
475 config->current_config = parent_config; 479 config->current_config_path = parent_config;
476 list_del(config->config_chain, index); 480 list_del(config->config_chain, index);
477 return false; 481 return false;
478 } 482 }
479 483
480 // restore current_config 484 // restore current_config_path
481 config->current_config = parent_config; 485 config->current_config_path = parent_config;
482 return true; 486 return true;
483} 487}
484 488
485bool load_include_configs(const char *path, struct sway_config *config) { 489bool load_include_configs(const char *path, struct sway_config *config) {
486 char *wd = getcwd(NULL, 0); 490 char *wd = getcwd(NULL, 0);
487 char *parent_path = strdup(config->current_config); 491 char *parent_path = strdup(config->current_config_path);
488 const char *parent_dir = dirname(parent_path); 492 const char *parent_dir = dirname(parent_path);
489 493
490 if (chdir(parent_dir) < 0) { 494 if (chdir(parent_dir) < 0) {
@@ -512,7 +516,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
512 // restore wd 516 // restore wd
513 if (chdir(wd) < 0) { 517 if (chdir(wd) < 0) {
514 free(wd); 518 free(wd);
515 wlr_log(L_ERROR, "failed to restore working directory"); 519 wlr_log(WLR_ERROR, "failed to restore working directory");
516 return false; 520 return false;
517 } 521 }
518 522
@@ -527,13 +531,13 @@ static int detect_brace_on_following_line(FILE *file, char *line,
527 char *peeked = NULL; 531 char *peeked = NULL;
528 long position = 0; 532 long position = 0;
529 do { 533 do {
530 wlr_log(L_DEBUG, "Peeking line %d", line_number + lines + 1); 534 wlr_log(WLR_DEBUG, "Peeking line %d", line_number + lines + 1);
531 free(peeked); 535 free(peeked);
532 peeked = peek_line(file, lines, &position); 536 peeked = peek_line(file, lines, &position);
533 if (peeked) { 537 if (peeked) {
534 peeked = strip_whitespace(peeked); 538 peeked = strip_whitespace(peeked);
535 } 539 }
536 wlr_log(L_DEBUG, "Peeked line: `%s`", peeked); 540 wlr_log(WLR_DEBUG, "Peeked line: `%s`", peeked);
537 lines++; 541 lines++;
538 } while (peeked && strlen(peeked) == 0); 542 } while (peeked && strlen(peeked) == 0);
539 543
@@ -552,7 +556,7 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
552 + (add_brace ? 2 : 0) + 1; 556 + (add_brace ? 2 : 0) + 1;
553 char *expanded = calloc(1, size); 557 char *expanded = calloc(1, size);
554 if (!expanded) { 558 if (!expanded) {
555 wlr_log(L_ERROR, "Cannot allocate expanded line buffer"); 559 wlr_log(WLR_ERROR, "Cannot allocate expanded line buffer");
556 return NULL; 560 return NULL;
557 } 561 }
558 snprintf(expanded, size, "%s%s%s%s", block ? block : "", 562 snprintf(expanded, size, "%s%s%s%s", block ? block : "",
@@ -561,10 +565,33 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
561} 565}
562 566
563bool read_config(FILE *file, struct sway_config *config) { 567bool read_config(FILE *file, struct sway_config *config) {
568 bool reading_main_config = false;
569 char *this_config = NULL;
570 size_t config_size = 0;
571 if (config->current_config == NULL) {
572 reading_main_config = true;
573
574 int ret_seek = fseek(file, 0, SEEK_END);
575 long ret_tell = ftell(file);
576 if (ret_seek == -1 || ret_tell == -1) {
577 wlr_log(WLR_ERROR, "Unable to get size of config file");
578 return false;
579 }
580 config_size = ret_tell;
581 rewind(file);
582
583 config->current_config = this_config = calloc(1, config_size + 1);
584 if (this_config == NULL) {
585 wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents");
586 return false;
587 }
588 }
589
564 bool success = true; 590 bool success = true;
565 int line_number = 0; 591 int line_number = 0;
566 char *line; 592 char *line;
567 list_t *stack = create_list(); 593 list_t *stack = create_list();
594 size_t read = 0;
568 while (!feof(file)) { 595 while (!feof(file)) {
569 char *block = stack->length ? stack->items[0] : NULL; 596 char *block = stack->length ? stack->items[0] : NULL;
570 line = read_line(file); 597 line = read_line(file);
@@ -572,7 +599,26 @@ bool read_config(FILE *file, struct sway_config *config) {
572 continue; 599 continue;
573 } 600 }
574 line_number++; 601 line_number++;
575 wlr_log(L_DEBUG, "Read line %d: %s", line_number, line); 602 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
603
604 if (reading_main_config) {
605 size_t length = strlen(line);
606
607 if (read + length > config_size) {
608 wlr_log(WLR_ERROR, "Config file changed during reading");
609 list_foreach(stack, free);
610 list_free(stack);
611 free(line);
612 return false;
613 }
614
615 strcpy(this_config + read, line);
616 if (line_number != 1) {
617 this_config[read - 1] = '\n';
618 }
619 read += length + 1;
620 }
621
576 line = strip_whitespace(line); 622 line = strip_whitespace(line);
577 if (line[0] == '#') { 623 if (line[0] == '#') {
578 free(line); 624 free(line);
@@ -586,15 +632,16 @@ bool read_config(FILE *file, struct sway_config *config) {
586 line_number); 632 line_number);
587 if (brace_detected > 0) { 633 if (brace_detected > 0) {
588 line_number += brace_detected; 634 line_number += brace_detected;
589 wlr_log(L_DEBUG, "Detected open brace on line %d", line_number); 635 wlr_log(WLR_DEBUG, "Detected open brace on line %d", line_number);
590 } 636 }
591 char *expanded = expand_line(block, line, brace_detected > 0); 637 char *expanded = expand_line(block, line, brace_detected > 0);
592 if (!expanded) { 638 if (!expanded) {
593 list_foreach(stack, free); 639 list_foreach(stack, free);
594 list_free(stack); 640 list_free(stack);
641 free(line);
595 return false; 642 return false;
596 } 643 }
597 wlr_log(L_DEBUG, "Expanded line: %s", expanded); 644 wlr_log(WLR_DEBUG, "Expanded line: %s", expanded);
598 struct cmd_results *res; 645 struct cmd_results *res;
599 if (block && strcmp(block, "<commands>") == 0) { 646 if (block && strcmp(block, "<commands>") == 0) {
600 // Special case 647 // Special case
@@ -606,23 +653,23 @@ bool read_config(FILE *file, struct sway_config *config) {
606 switch(res->status) { 653 switch(res->status) {
607 case CMD_FAILURE: 654 case CMD_FAILURE:
608 case CMD_INVALID: 655 case CMD_INVALID:
609 wlr_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, 656 wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number,
610 line, res->error, config->current_config); 657 line, res->error, config->current_config_path);
611 success = false; 658 success = false;
612 break; 659 break;
613 660
614 case CMD_DEFER: 661 case CMD_DEFER:
615 wlr_log(L_DEBUG, "Deferring command `%s'", line); 662 wlr_log(WLR_DEBUG, "Deferring command `%s'", line);
616 list_add(config->cmd_queue, strdup(line)); 663 list_add(config->cmd_queue, strdup(line));
617 break; 664 break;
618 665
619 case CMD_BLOCK_COMMANDS: 666 case CMD_BLOCK_COMMANDS:
620 wlr_log(L_DEBUG, "Entering commands block"); 667 wlr_log(WLR_DEBUG, "Entering commands block");
621 list_insert(stack, 0, "<commands>"); 668 list_insert(stack, 0, "<commands>");
622 break; 669 break;
623 670
624 case CMD_BLOCK: 671 case CMD_BLOCK:
625 wlr_log(L_DEBUG, "Entering block '%s'", res->input); 672 wlr_log(WLR_DEBUG, "Entering block '%s'", res->input);
626 list_insert(stack, 0, strdup(res->input)); 673 list_insert(stack, 0, strdup(res->input));
627 if (strcmp(res->input, "bar") == 0) { 674 if (strcmp(res->input, "bar") == 0) {
628 config->current_bar = NULL; 675 config->current_bar = NULL;
@@ -631,7 +678,7 @@ bool read_config(FILE *file, struct sway_config *config) {
631 678
632 case CMD_BLOCK_END: 679 case CMD_BLOCK_END:
633 if (!block) { 680 if (!block) {
634 wlr_log(L_DEBUG, "Unmatched '}' on line %i", line_number); 681 wlr_log(WLR_DEBUG, "Unmatched '}' on line %i", line_number);
635 success = false; 682 success = false;
636 break; 683 break;
637 } 684 }
@@ -639,7 +686,7 @@ bool read_config(FILE *file, struct sway_config *config) {
639 config->current_bar = NULL; 686 config->current_bar = NULL;
640 } 687 }
641 688
642 wlr_log(L_DEBUG, "Exiting block '%s'", block); 689 wlr_log(WLR_DEBUG, "Exiting block '%s'", block);
643 list_del(stack, 0); 690 list_del(stack, 0);
644 free(block); 691 free(block);
645 memset(&config->handler_context, 0, 692 memset(&config->handler_context, 0,
@@ -682,7 +729,7 @@ char *do_var_replacement(char *str) {
682 int vvlen = strlen(var->value); 729 int vvlen = strlen(var->value);
683 char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); 730 char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
684 if (!newstr) { 731 if (!newstr) {
685 wlr_log(L_ERROR, 732 wlr_log(WLR_ERROR,
686 "Unable to allocate replacement " 733 "Unable to allocate replacement "
687 "during variable expansion"); 734 "during variable expansion");
688 break; 735 break;
@@ -744,6 +791,6 @@ void config_update_font_height(bool recalculate) {
744 } 791 }
745 792
746 if (config->font_height != prev_max_height) { 793 if (config->font_height != prev_max_height) {
747 arrange_and_commit(&root_container); 794 arrange_windows(&root_container);
748 } 795 }
749} 796}
diff --git a/sway/config/bar.c b/sway/config/bar.c
index ee062c6a..3a74331e 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -16,10 +16,10 @@
16#include "log.h" 16#include "log.h"
17 17
18static void terminate_swaybar(pid_t pid) { 18static void terminate_swaybar(pid_t pid) {
19 wlr_log(L_DEBUG, "Terminating swaybar %d", pid); 19 wlr_log(WLR_DEBUG, "Terminating swaybar %d", pid);
20 int ret = kill(-pid, SIGTERM); 20 int ret = kill(-pid, SIGTERM);
21 if (ret != 0) { 21 if (ret != 0) {
22 wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); 22 wlr_log_errno(WLR_ERROR, "Unable to terminate swaybar %d", pid);
23 } else { 23 } else {
24 int status; 24 int status;
25 waitpid(pid, &status, 0); 25 waitpid(pid, &status, 0);
@@ -167,7 +167,7 @@ void invoke_swaybar(struct bar_config *bar) {
167 // Pipe to communicate errors 167 // Pipe to communicate errors
168 int filedes[2]; 168 int filedes[2];
169 if (pipe(filedes) == -1) { 169 if (pipe(filedes) == -1) {
170 wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); 170 wlr_log(WLR_ERROR, "Pipe setup failed! Cannot fork into bar");
171 return; 171 return;
172 } 172 }
173 173
@@ -197,17 +197,17 @@ void invoke_swaybar(struct bar_config *bar) {
197 execvp(cmd[0], cmd); 197 execvp(cmd[0], cmd);
198 exit(1); 198 exit(1);
199 } 199 }
200 wlr_log(L_DEBUG, "Spawned swaybar %d", bar->pid); 200 wlr_log(WLR_DEBUG, "Spawned swaybar %d", bar->pid);
201 close(filedes[0]); 201 close(filedes[0]);
202 size_t len; 202 size_t len;
203 if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) { 203 if (read(filedes[1], &len, sizeof(size_t)) == sizeof(size_t)) {
204 char *buf = malloc(len); 204 char *buf = malloc(len);
205 if(!buf) { 205 if(!buf) {
206 wlr_log(L_ERROR, "Cannot allocate error string"); 206 wlr_log(WLR_ERROR, "Cannot allocate error string");
207 return; 207 return;
208 } 208 }
209 if (read(filedes[1], buf, len)) { 209 if (read(filedes[1], buf, len)) {
210 wlr_log(L_ERROR, "%s", buf); 210 wlr_log(WLR_ERROR, "%s", buf);
211 } 211 }
212 free(buf); 212 free(buf);
213 } 213 }
@@ -244,7 +244,7 @@ void load_swaybars() {
244 if (bar->pid != 0) { 244 if (bar->pid != 0) {
245 terminate_swaybar(bar->pid); 245 terminate_swaybar(bar->pid);
246 } 246 }
247 wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); 247 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
248 invoke_swaybar(bar); 248 invoke_swaybar(bar);
249 } 249 }
250 } 250 }
diff --git a/sway/config/input.c b/sway/config/input.c
index 17303ccc..8d687a6d 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -8,17 +8,18 @@
8struct input_config *new_input_config(const char* identifier) { 8struct input_config *new_input_config(const char* identifier) {
9 struct input_config *input = calloc(1, sizeof(struct input_config)); 9 struct input_config *input = calloc(1, sizeof(struct input_config));
10 if (!input) { 10 if (!input) {
11 wlr_log(L_DEBUG, "Unable to allocate input config"); 11 wlr_log(WLR_DEBUG, "Unable to allocate input config");
12 return NULL; 12 return NULL;
13 } 13 }
14 wlr_log(L_DEBUG, "new_input_config(%s)", identifier); 14 wlr_log(WLR_DEBUG, "new_input_config(%s)", identifier);
15 if (!(input->identifier = strdup(identifier))) { 15 if (!(input->identifier = strdup(identifier))) {
16 free(input); 16 free(input);
17 wlr_log(L_DEBUG, "Unable to allocate input config"); 17 wlr_log(WLR_DEBUG, "Unable to allocate input config");
18 return NULL; 18 return NULL;
19 } 19 }
20 20
21 input->tap = INT_MIN; 21 input->tap = INT_MIN;
22 input->tap_button_map = INT_MIN;
22 input->drag_lock = INT_MIN; 23 input->drag_lock = INT_MIN;
23 input->dwt = INT_MIN; 24 input->dwt = INT_MIN;
24 input->send_events = INT_MIN; 25 input->send_events = INT_MIN;
@@ -27,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) {
27 input->natural_scroll = INT_MIN; 28 input->natural_scroll = INT_MIN;
28 input->accel_profile = INT_MIN; 29 input->accel_profile = INT_MIN;
29 input->pointer_accel = FLT_MIN; 30 input->pointer_accel = FLT_MIN;
31 input->scroll_button = INT_MIN;
30 input->scroll_method = INT_MIN; 32 input->scroll_method = INT_MIN;
31 input->left_handed = INT_MIN; 33 input->left_handed = INT_MIN;
32 input->repeat_delay = INT_MIN; 34 input->repeat_delay = INT_MIN;
@@ -70,12 +72,18 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
70 if (src->scroll_method != INT_MIN) { 72 if (src->scroll_method != INT_MIN) {
71 dst->scroll_method = src->scroll_method; 73 dst->scroll_method = src->scroll_method;
72 } 74 }
75 if (src->scroll_button != INT_MIN) {
76 dst->scroll_button = src->scroll_button;
77 }
73 if (src->send_events != INT_MIN) { 78 if (src->send_events != INT_MIN) {
74 dst->send_events = src->send_events; 79 dst->send_events = src->send_events;
75 } 80 }
76 if (src->tap != INT_MIN) { 81 if (src->tap != INT_MIN) {
77 dst->tap = src->tap; 82 dst->tap = src->tap;
78 } 83 }
84 if (src->tap_button_map != INT_MIN) {
85 dst->tap_button_map = src->tap_button_map;
86 }
79 if (src->xkb_layout) { 87 if (src->xkb_layout) {
80 free(dst->xkb_layout); 88 free(dst->xkb_layout);
81 dst->xkb_layout = strdup(src->xkb_layout); 89 dst->xkb_layout = strdup(src->xkb_layout);
@@ -112,7 +120,7 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
112struct input_config *copy_input_config(struct input_config *ic) { 120struct input_config *copy_input_config(struct input_config *ic) {
113 struct input_config *copy = calloc(1, sizeof(struct input_config)); 121 struct input_config *copy = calloc(1, sizeof(struct input_config));
114 if (copy == NULL) { 122 if (copy == NULL) {
115 wlr_log(L_ERROR, "could not allocate input config"); 123 wlr_log(WLR_ERROR, "could not allocate input config");
116 return NULL; 124 return NULL;
117 } 125 }
118 merge_input_config(copy, ic); 126 merge_input_config(copy, ic);
diff --git a/sway/config/output.c b/sway/config/output.c
index 648ded27..1bf9e5f1 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -90,7 +90,7 @@ static void set_mode(struct wlr_output *output, int width, int height,
90 float refresh_rate) { 90 float refresh_rate) {
91 int mhz = (int)(refresh_rate * 1000); 91 int mhz = (int)(refresh_rate * 1000);
92 if (wl_list_empty(&output->modes)) { 92 if (wl_list_empty(&output->modes)) {
93 wlr_log(L_DEBUG, "Assigning custom mode to %s", output->name); 93 wlr_log(WLR_DEBUG, "Assigning custom mode to %s", output->name);
94 wlr_output_set_custom_mode(output, width, height, mhz); 94 wlr_output_set_custom_mode(output, width, height, mhz);
95 return; 95 return;
96 } 96 }
@@ -106,9 +106,9 @@ static void set_mode(struct wlr_output *output, int width, int height,
106 } 106 }
107 } 107 }
108 if (!best) { 108 if (!best) {
109 wlr_log(L_ERROR, "Configured mode for %s not available", output->name); 109 wlr_log(WLR_ERROR, "Configured mode for %s not available", output->name);
110 } else { 110 } else {
111 wlr_log(L_DEBUG, "Assigning configured mode to %s", output->name); 111 wlr_log(WLR_DEBUG, "Assigning configured mode to %s", output->name);
112 wlr_output_set_mode(output, best); 112 wlr_output_set_mode(output, best);
113 } 113 }
114} 114}
@@ -116,7 +116,7 @@ static void set_mode(struct wlr_output *output, int width, int height,
116void terminate_swaybg(pid_t pid) { 116void terminate_swaybg(pid_t pid) {
117 int ret = kill(pid, SIGTERM); 117 int ret = kill(pid, SIGTERM);
118 if (ret != 0) { 118 if (ret != 0) {
119 wlr_log(L_ERROR, "Unable to terminate swaybg [pid: %d]", pid); 119 wlr_log(WLR_ERROR, "Unable to terminate swaybg [pid: %d]", pid);
120 } else { 120 } else {
121 int status; 121 int status;
122 waitpid(pid, &status, 0); 122 waitpid(pid, &status, 0);
@@ -144,22 +144,22 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
144 } 144 }
145 145
146 if (oc && oc->width > 0 && oc->height > 0) { 146 if (oc && oc->width > 0 && oc->height > 0) {
147 wlr_log(L_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, 147 wlr_log(WLR_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width,
148 oc->height, oc->refresh_rate); 148 oc->height, oc->refresh_rate);
149 set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); 149 set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate);
150 } 150 }
151 if (oc && oc->scale > 0) { 151 if (oc && oc->scale > 0) {
152 wlr_log(L_DEBUG, "Set %s scale to %f", oc->name, oc->scale); 152 wlr_log(WLR_DEBUG, "Set %s scale to %f", oc->name, oc->scale);
153 wlr_output_set_scale(wlr_output, oc->scale); 153 wlr_output_set_scale(wlr_output, oc->scale);
154 } 154 }
155 if (oc && oc->transform >= 0) { 155 if (oc && oc->transform >= 0) {
156 wlr_log(L_DEBUG, "Set %s transform to %d", oc->name, oc->transform); 156 wlr_log(WLR_DEBUG, "Set %s transform to %d", oc->name, oc->transform);
157 wlr_output_set_transform(wlr_output, oc->transform); 157 wlr_output_set_transform(wlr_output, oc->transform);
158 } 158 }
159 159
160 // Find position for it 160 // Find position for it
161 if (oc && (oc->x != -1 || oc->y != -1)) { 161 if (oc && (oc->x != -1 || oc->y != -1)) {
162 wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); 162 wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
163 wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); 163 wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y);
164 } else { 164 } else {
165 wlr_output_layout_add_auto(output_layout, wlr_output); 165 wlr_output_layout_add_auto(output_layout, wlr_output);
@@ -187,7 +187,7 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
187 terminate_swaybg(output->sway_output->bg_pid); 187 terminate_swaybg(output->sway_output->bg_pid);
188 } 188 }
189 189
190 wlr_log(L_DEBUG, "Setting background for output %d to %s", 190 wlr_log(WLR_DEBUG, "Setting background for output %d to %s",
191 output_i, oc->background); 191 output_i, oc->background);
192 192
193 size_t len = snprintf(NULL, 0, "%s %d %s %s", 193 size_t len = snprintf(NULL, 0, "%s %d %s %s",
@@ -195,28 +195,30 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
195 output_i, oc->background, oc->background_option); 195 output_i, oc->background, oc->background_option);
196 char *command = malloc(len + 1); 196 char *command = malloc(len + 1);
197 if (!command) { 197 if (!command) {
198 wlr_log(L_DEBUG, "Unable to allocate swaybg command"); 198 wlr_log(WLR_DEBUG, "Unable to allocate swaybg command");
199 return; 199 return;
200 } 200 }
201 snprintf(command, len + 1, "%s %d %s %s", 201 snprintf(command, len + 1, "%s %d %s %s",
202 config->swaybg_command ? config->swaybg_command : "swaybg", 202 config->swaybg_command ? config->swaybg_command : "swaybg",
203 output_i, oc->background, oc->background_option); 203 output_i, oc->background, oc->background_option);
204 wlr_log(L_DEBUG, "-> %s", command); 204 wlr_log(WLR_DEBUG, "-> %s", command);
205 205
206 char *const cmd[] = { "sh", "-c", command, NULL }; 206 char *const cmd[] = { "sh", "-c", command, NULL };
207 output->sway_output->bg_pid = fork(); 207 output->sway_output->bg_pid = fork();
208 if (output->sway_output->bg_pid == 0) { 208 if (output->sway_output->bg_pid == 0) {
209 execvp(cmd[0], cmd); 209 execvp(cmd[0], cmd);
210 } else {
211 free(command);
210 } 212 }
211 } 213 }
212 if (oc && oc->dpms_state != DPMS_IGNORE) { 214 if (oc && oc->dpms_state != DPMS_IGNORE) {
213 switch (oc->dpms_state) { 215 switch (oc->dpms_state) {
214 case DPMS_ON: 216 case DPMS_ON:
215 wlr_log(L_DEBUG, "Turning on screen"); 217 wlr_log(WLR_DEBUG, "Turning on screen");
216 wlr_output_enable(wlr_output, true); 218 wlr_output_enable(wlr_output, true);
217 break; 219 break;
218 case DPMS_OFF: 220 case DPMS_OFF:
219 wlr_log(L_DEBUG, "Turning off screen"); 221 wlr_log(WLR_DEBUG, "Turning off screen");
220 wlr_output_enable(wlr_output, false); 222 wlr_output_enable(wlr_output, false);
221 break; 223 break;
222 case DPMS_IGNORE: 224 case DPMS_IGNORE:
diff --git a/sway/config/seat.c b/sway/config/seat.c
index bd8b45c8..83dac4c0 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -7,11 +7,11 @@
7struct seat_config *new_seat_config(const char* name) { 7struct seat_config *new_seat_config(const char* name) {
8 struct seat_config *seat = calloc(1, sizeof(struct seat_config)); 8 struct seat_config *seat = calloc(1, sizeof(struct seat_config));
9 if (!seat) { 9 if (!seat) {
10 wlr_log(L_DEBUG, "Unable to allocate seat config"); 10 wlr_log(WLR_DEBUG, "Unable to allocate seat config");
11 return NULL; 11 return NULL;
12 } 12 }
13 13
14 wlr_log(L_DEBUG, "new_seat_config(%s)", name); 14 wlr_log(WLR_DEBUG, "new_seat_config(%s)", name);
15 seat->name = strdup(name); 15 seat->name = strdup(name);
16 if (!sway_assert(seat->name, "could not allocate name for seat")) { 16 if (!sway_assert(seat->name, "could not allocate name for seat")) {
17 free(seat); 17 free(seat);
@@ -34,7 +34,7 @@ struct seat_attachment_config *seat_attachment_config_new() {
34 struct seat_attachment_config *attachment = 34 struct seat_attachment_config *attachment =
35 calloc(1, sizeof(struct seat_attachment_config)); 35 calloc(1, sizeof(struct seat_attachment_config));
36 if (!attachment) { 36 if (!attachment) {
37 wlr_log(L_DEBUG, "cannot allocate attachment config"); 37 wlr_log(WLR_DEBUG, "cannot allocate attachment config");
38 return NULL; 38 return NULL;
39 } 39 }
40 return attachment; 40 return attachment;
diff --git a/sway/criteria.c b/sway/criteria.c
index d9f09ecc..e2b248de 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -37,7 +37,7 @@ void criteria_destroy(struct criteria *criteria) {
37 pcre_free(criteria->con_mark); 37 pcre_free(criteria->con_mark);
38 pcre_free(criteria->window_role); 38 pcre_free(criteria->window_role);
39 free(criteria->workspace); 39 free(criteria->workspace);
40 40 free(criteria->cmdlist);
41 free(criteria->raw); 41 free(criteria->raw);
42 free(criteria); 42 free(criteria);
43} 43}
@@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) {
46 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); 46 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
47} 47}
48 48
49static int cmp_urgent(const void *_a, const void *_b) {
50 struct sway_view *a = *(void **)_a;
51 struct sway_view *b = *(void **)_b;
52
53 if (a->urgent.tv_sec < b->urgent.tv_sec) {
54 return -1;
55 } else if (a->urgent.tv_sec > b->urgent.tv_sec) {
56 return 1;
57 }
58 if (a->urgent.tv_nsec < b->urgent.tv_nsec) {
59 return -1;
60 } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) {
61 return 1;
62 }
63 return 0;
64}
65
66static void find_urgent_iterator(struct sway_container *swayc, void *data) {
67 if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) {
68 return;
69 }
70 list_t *urgent_views = data;
71 list_add(urgent_views, swayc->sway_view);
72}
73
49static bool criteria_matches_view(struct criteria *criteria, 74static bool criteria_matches_view(struct criteria *criteria,
50 struct sway_view *view) { 75 struct sway_view *view) {
51 if (criteria->title) { 76 if (criteria->title) {
@@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria,
133 } 158 }
134 159
135 if (criteria->urgent) { 160 if (criteria->urgent) {
136 // TODO 161 if (!view_is_urgent(view)) {
137 return false; 162 return false;
163 }
164 list_t *urgent_views = create_list();
165 container_for_each_descendant_dfs(&root_container,
166 find_urgent_iterator, urgent_views);
167 list_stable_sort(urgent_views, cmp_urgent);
168 struct sway_view *target;
169 if (criteria->urgent == 'o') { // oldest
170 target = urgent_views->items[0];
171 } else { // latest
172 target = urgent_views->items[urgent_views->length - 1];
173 }
174 list_free(urgent_views);
175 if (view != target) {
176 return false;
177 }
138 } 178 }
139 179
140 if (criteria->workspace) { 180 if (criteria->workspace) {
@@ -507,7 +547,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
507 } 547 }
508 unescape(value); 548 unescape(value);
509 } 549 }
510 wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); 550 wlr_log(WLR_DEBUG, "Found pair: %s=%s", name, value);
511 if (!parse_token(criteria, name, value)) { 551 if (!parse_token(criteria, name, value)) {
512 *error_arg = error; 552 *error_arg = error;
513 goto cleanup; 553 goto cleanup;
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index e495790c..6575519d 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
13 } 13 }
14 } 14 }
15} 15}
16
17void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root_container.children->length; ++i) {
19 struct sway_container *cont = root_container.children->items[i];
20 if (cont->type == C_OUTPUT) {
21 output_damage_whole_container(cont->sway_output, con);
22 }
23 }
24}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
index c02ca26e..da17d0f2 100644
--- a/sway/desktop/idle_inhibit_v1.c
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -9,7 +9,7 @@
9static void handle_destroy(struct wl_listener *listener, void *data) { 9static void handle_destroy(struct wl_listener *listener, void *data) {
10 struct sway_idle_inhibitor_v1 *inhibitor = 10 struct sway_idle_inhibitor_v1 *inhibitor =
11 wl_container_of(listener, inhibitor, destroy); 11 wl_container_of(listener, inhibitor, destroy);
12 wlr_log(L_DEBUG, "Sway idle inhibitor destroyed"); 12 wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed");
13 wl_list_remove(&inhibitor->link); 13 wl_list_remove(&inhibitor->link);
14 wl_list_remove(&inhibitor->destroy.link); 14 wl_list_remove(&inhibitor->destroy.link);
15 idle_inhibit_v1_check_active(inhibitor->manager); 15 idle_inhibit_v1_check_active(inhibitor->manager);
@@ -20,7 +20,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
20 struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data; 20 struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
21 struct sway_idle_inhibit_manager_v1 *manager = 21 struct sway_idle_inhibit_manager_v1 *manager =
22 wl_container_of(listener, manager, new_idle_inhibitor_v1); 22 wl_container_of(listener, manager, new_idle_inhibitor_v1);
23 wlr_log(L_DEBUG, "New sway idle inhibitor"); 23 wlr_log(WLR_DEBUG, "New sway idle inhibitor");
24 24
25 struct sway_idle_inhibitor_v1 *inhibitor = 25 struct sway_idle_inhibitor_v1 *inhibitor =
26 calloc(1, sizeof(struct sway_idle_inhibitor_v1)); 26 calloc(1, sizeof(struct sway_idle_inhibitor_v1));
@@ -67,6 +67,7 @@ struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(
67 67
68 manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); 68 manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display);
69 if (!manager->wlr_manager) { 69 if (!manager->wlr_manager) {
70 free(manager);
70 return NULL; 71 return NULL;
71 } 72 }
72 manager->idle = idle; 73 manager->idle = idle;
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index de1fe349..a7d96717 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
12#include "sway/layers.h" 12#include "sway/layers.h"
13#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h" 14#include "sway/server.h"
15#include "sway/tree/arrange.h"
16#include "sway/tree/layout.h" 15#include "sway/tree/layout.h"
17#include "log.h" 16#include "log.h"
18 17
@@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) {
174 173
175 if (memcmp(&usable_area, &output->usable_area, 174 if (memcmp(&usable_area, &output->usable_area,
176 sizeof(struct wlr_box)) != 0) { 175 sizeof(struct wlr_box)) != 0) {
177 wlr_log(L_DEBUG, "Usable area changed, rearranging output"); 176 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 177 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_and_commit(output->swayc); 178 container_set_dirty(output->swayc);
180 } 179 }
181 180
182 // Arrange non-exlusive surfaces from top->bottom 181 // Arrange non-exlusive surfaces from top->bottom
@@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
269static void handle_destroy(struct wl_listener *listener, void *data) { 268static void handle_destroy(struct wl_listener *listener, void *data) {
270 struct sway_layer_surface *sway_layer = 269 struct sway_layer_surface *sway_layer =
271 wl_container_of(listener, sway_layer, destroy); 270 wl_container_of(listener, sway_layer, destroy);
272 wlr_log(L_DEBUG, "Layer surface destroyed (%s)", 271 wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)",
273 sway_layer->layer_surface->namespace); 272 sway_layer->layer_surface->namespace);
274 if (sway_layer->layer_surface->mapped) { 273 if (sway_layer->layer_surface->mapped) {
275 unmap(sway_layer); 274 unmap(sway_layer);
@@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
316 struct wlr_layer_surface *layer_surface = data; 315 struct wlr_layer_surface *layer_surface = data;
317 struct sway_server *server = 316 struct sway_server *server =
318 wl_container_of(listener, server, layer_shell_surface); 317 wl_container_of(listener, server, layer_shell_surface);
319 wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d " 318 wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
320 "size %dx%d margin %d,%d,%d,%d", 319 "size %dx%d margin %d,%d,%d,%d",
321 layer_surface->namespace, layer_surface->layer, layer_surface->layer, 320 layer_surface->namespace, layer_surface->layer, layer_surface->layer,
322 layer_surface->client_pending.desired_width, 321 layer_surface->client_pending.desired_width,
@@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
326 layer_surface->client_pending.margin.bottom, 325 layer_surface->client_pending.margin.bottom,
327 layer_surface->client_pending.margin.left); 326 layer_surface->client_pending.margin.left);
328 327
329 struct sway_layer_surface *sway_layer =
330 calloc(1, sizeof(struct sway_layer_surface));
331 if (!sway_layer) {
332 return;
333 }
334
335 if (!layer_surface->output) { 328 if (!layer_surface->output) {
336 // Assign last active output 329 // Assign last active output
337 struct sway_container *output = NULL; 330 struct sway_container *output = NULL;
@@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
353 layer_surface->output = output->sway_output->wlr_output; 346 layer_surface->output = output->sway_output->wlr_output;
354 } 347 }
355 348
349 struct sway_layer_surface *sway_layer =
350 calloc(1, sizeof(struct sway_layer_surface));
351 if (!sway_layer) {
352 return;
353 }
354
356 sway_layer->surface_commit.notify = handle_surface_commit; 355 sway_layer->surface_commit.notify = handle_surface_commit;
357 wl_signal_add(&layer_surface->surface->events.commit, 356 wl_signal_add(&layer_surface->surface->events.commit,
358 &sway_layer->surface_commit); 357 &sway_layer->surface_commit);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 8b50bc44..a9808406 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -204,11 +204,11 @@ bool output_has_opaque_lockscreen(struct sway_output *output,
204 }; 204 };
205 pixman_region32_t surface_opaque_box; 205 pixman_region32_t surface_opaque_box;
206 pixman_region32_init(&surface_opaque_box); 206 pixman_region32_init(&surface_opaque_box);
207 pixman_region32_copy(&surface_opaque_box, &wlr_surface->current.opaque); 207 pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
208 pixman_region32_translate(&surface_opaque_box, 208 pixman_region32_translate(&surface_opaque_box,
209 sway_layer_surface->geo.x, sway_layer_surface->geo.y); 209 sway_layer_surface->geo.x, sway_layer_surface->geo.y);
210 bool contains = pixman_region32_contains_rectangle( 210 bool contains = pixman_region32_contains_rectangle(&surface_opaque_box,
211 &wlr_surface->current.opaque, &output_box); 211 &output_box);
212 pixman_region32_fini(&surface_opaque_box); 212 pixman_region32_fini(&surface_opaque_box);
213 if (contains) { 213 if (contains) {
214 return true; 214 return true;
@@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
492 output->wlr_output->data = NULL; 492 output->wlr_output->data = NULL;
493 free(output); 493 free(output);
494 494
495 arrange_and_commit(&root_container); 495 arrange_windows(&root_container);
496} 496}
497 497
498static void handle_mode(struct wl_listener *listener, void *data) { 498static void handle_mode(struct wl_listener *listener, void *data) {
499 struct sway_output *output = wl_container_of(listener, output, mode); 499 struct sway_output *output = wl_container_of(listener, output, mode);
500 arrange_layers(output); 500 arrange_layers(output);
501 arrange_and_commit(output->swayc); 501 arrange_windows(output->swayc);
502 transaction_commit_dirty();
502} 503}
503 504
504static void handle_transform(struct wl_listener *listener, void *data) { 505static void handle_transform(struct wl_listener *listener, void *data) {
505 struct sway_output *output = wl_container_of(listener, output, transform); 506 struct sway_output *output = wl_container_of(listener, output, transform);
506 arrange_layers(output); 507 arrange_layers(output);
507 arrange_and_commit(output->swayc); 508 arrange_windows(output->swayc);
509 transaction_commit_dirty();
508} 510}
509 511
510static void handle_scale_iterator(struct sway_container *view, void *data) { 512static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
515 struct sway_output *output = wl_container_of(listener, output, scale); 517 struct sway_output *output = wl_container_of(listener, output, scale);
516 arrange_layers(output); 518 arrange_layers(output);
517 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 519 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
518 arrange_and_commit(output->swayc); 520 arrange_windows(output->swayc);
521 transaction_commit_dirty();
519} 522}
520 523
521struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { 524struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -525,7 +528,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
525void handle_new_output(struct wl_listener *listener, void *data) { 528void handle_new_output(struct wl_listener *listener, void *data) {
526 struct sway_server *server = wl_container_of(listener, server, new_output); 529 struct sway_server *server = wl_container_of(listener, server, new_output);
527 struct wlr_output *wlr_output = data; 530 struct wlr_output *wlr_output = data;
528 wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 531 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
529 532
530 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 533 struct sway_output *output = calloc(1, sizeof(struct sway_output));
531 if (!output) { 534 if (!output) {
@@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) {
584 output->damage_destroy.notify = damage_handle_destroy; 587 output->damage_destroy.notify = damage_handle_destroy;
585 588
586 arrange_layers(output); 589 arrange_layers(output);
587 arrange_and_commit(&root_container); 590 arrange_windows(&root_container);
591 transaction_commit_dirty();
588} 592}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 43948f29..4c85e516 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -15,6 +15,7 @@
15#include <wlr/util/region.h> 15#include <wlr/util/region.h>
16#include "log.h" 16#include "log.h"
17#include "sway/config.h" 17#include "sway/config.h"
18#include "sway/debug.h"
18#include "sway/input/input-manager.h" 19#include "sway/input/input-manager.h"
19#include "sway/input/seat.h" 20#include "sway/input/seat.h"
20#include "sway/layers.h" 21#include "sway/layers.h"
@@ -255,6 +256,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
255 render_view_surfaces(view, output, damage, view->swayc->alpha); 256 render_view_surfaces(view, output, damage, view->swayc->alpha);
256 } 257 }
257 258
259 if (view->using_csd) {
260 return;
261 }
262
258 struct wlr_box box; 263 struct wlr_box box;
259 float output_scale = output->wlr_output->scale; 264 float output_scale = output->wlr_output->scale;
260 float color[4]; 265 float color[4];
@@ -542,9 +547,6 @@ static void render_container(struct sway_output *output,
542static void render_container_simple(struct sway_output *output, 547static void render_container_simple(struct sway_output *output,
543 pixman_region32_t *damage, struct sway_container *con, 548 pixman_region32_t *damage, struct sway_container *con,
544 bool parent_focused) { 549 bool parent_focused) {
545 struct sway_seat *seat = input_manager_current_seat(input_manager);
546 struct sway_container *focus = seat_get_focus(seat);
547
548 for (int i = 0; i < con->current.children->length; ++i) { 550 for (int i = 0; i < con->current.children->length; ++i) {
549 struct sway_container *child = con->current.children->items[i]; 551 struct sway_container *child = con->current.children->items[i];
550 552
@@ -555,11 +557,15 @@ static void render_container_simple(struct sway_output *output,
555 struct wlr_texture *marks_texture; 557 struct wlr_texture *marks_texture;
556 struct sway_container_state *state = &child->current; 558 struct sway_container_state *state = &child->current;
557 559
558 if (focus == child || parent_focused) { 560 if (view_is_urgent(view)) {
561 colors = &config->border_colors.urgent;
562 title_texture = child->title_urgent;
563 marks_texture = view->marks_urgent;
564 } else if (state->focused || parent_focused) {
559 colors = &config->border_colors.focused; 565 colors = &config->border_colors.focused;
560 title_texture = child->title_focused; 566 title_texture = child->title_focused;
561 marks_texture = view->marks_focused; 567 marks_texture = view->marks_focused;
562 } else if (seat_get_focus_inactive(seat, con) == child) { 568 } else if (con->current.focused_inactive_child == child) {
563 colors = &config->border_colors.focused_inactive; 569 colors = &config->border_colors.focused_inactive;
564 title_texture = child->title_focused_inactive; 570 title_texture = child->title_focused_inactive;
565 marks_texture = view->marks_focused_inactive; 571 marks_texture = view->marks_focused_inactive;
@@ -569,17 +575,19 @@ static void render_container_simple(struct sway_output *output,
569 marks_texture = view->marks_unfocused; 575 marks_texture = view->marks_unfocused;
570 } 576 }
571 577
572 if (state->border == B_NORMAL) { 578 if (!view->using_csd) {
573 render_titlebar(output, damage, child, state->swayc_x, 579 if (state->border == B_NORMAL) {
574 state->swayc_y, state->swayc_width, colors, 580 render_titlebar(output, damage, child, state->swayc_x,
575 title_texture, marks_texture); 581 state->swayc_y, state->swayc_width, colors,
576 } else { 582 title_texture, marks_texture);
577 render_top_border(output, damage, child, colors); 583 } else {
584 render_top_border(output, damage, child, colors);
585 }
578 } 586 }
579 render_view(output, damage, child, colors); 587 render_view(output, damage, child, colors);
580 } else { 588 } else {
581 render_container(output, damage, child, 589 render_container(output, damage, child,
582 parent_focused || focus == child); 590 parent_focused || child->current.focused);
583 } 591 }
584 } 592 }
585} 593}
@@ -593,26 +601,34 @@ static void render_container_tabbed(struct sway_output *output,
593 if (!con->current.children->length) { 601 if (!con->current.children->length) {
594 return; 602 return;
595 } 603 }
596 struct sway_seat *seat = input_manager_current_seat(input_manager);
597 struct sway_container *focus = seat_get_focus(seat);
598 struct sway_container *current = seat_get_active_current_child(seat, con);
599 struct border_colors *current_colors = &config->border_colors.unfocused;
600 struct sway_container_state *pstate = &con->current; 604 struct sway_container_state *pstate = &con->current;
605 struct sway_container *current = pstate->focused_inactive_child;
606 struct border_colors *current_colors = &config->border_colors.unfocused;
607
608 double width_gap_adjustment = 2 * pstate->current_gaps;
609 int tab_width =
610 (pstate->swayc_width - width_gap_adjustment) / pstate->children->length;
601 611
602 // Render tabs 612 // Render tabs
603 for (int i = 0; i < con->current.children->length; ++i) { 613 for (int i = 0; i < pstate->children->length; ++i) {
604 struct sway_container *child = con->current.children->items[i]; 614 struct sway_container *child = pstate->children->items[i];
605 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 615 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
606 struct sway_container_state *cstate = &child->current; 616 struct sway_container_state *cstate = &child->current;
607 struct border_colors *colors; 617 struct border_colors *colors;
608 struct wlr_texture *title_texture; 618 struct wlr_texture *title_texture;
609 struct wlr_texture *marks_texture; 619 struct wlr_texture *marks_texture;
610 620 bool urgent = view ?
611 if (focus == child || parent_focused) { 621 view_is_urgent(view) : container_has_urgent_child(child);
622
623 if (urgent) {
624 colors = &config->border_colors.urgent;
625 title_texture = child->title_urgent;
626 marks_texture = view ? view->marks_urgent : NULL;
627 } else if (cstate->focused || parent_focused) {
612 colors = &config->border_colors.focused; 628 colors = &config->border_colors.focused;
613 title_texture = child->title_focused; 629 title_texture = child->title_focused;
614 marks_texture = view ? view->marks_focused : NULL; 630 marks_texture = view ? view->marks_focused : NULL;
615 } else if (child == current) { 631 } else if (child == pstate->focused_inactive_child) {
616 colors = &config->border_colors.focused_inactive; 632 colors = &config->border_colors.focused_inactive;
617 title_texture = child->title_focused_inactive; 633 title_texture = child->title_focused_inactive;
618 marks_texture = view ? view->marks_focused_inactive : NULL; 634 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -622,11 +638,12 @@ static void render_container_tabbed(struct sway_output *output,
622 marks_texture = view ? view->marks_unfocused : NULL; 638 marks_texture = view ? view->marks_unfocused : NULL;
623 } 639 }
624 640
625 int tab_width = pstate->swayc_width / pstate->children->length; 641 int x = cstate->swayc_x + tab_width * i;
626 int x = pstate->swayc_x + tab_width * i; 642
627 // Make last tab use the remaining width of the parent 643 // Make last tab use the remaining width of the parent
628 if (i == pstate->children->length - 1) { 644 if (i == pstate->children->length - 1) {
629 tab_width = pstate->swayc_width - tab_width * i; 645 tab_width =
646 pstate->swayc_width - width_gap_adjustment - tab_width * i;
630 } 647 }
631 648
632 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, 649 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
@@ -638,13 +655,11 @@ static void render_container_tabbed(struct sway_output *output,
638 } 655 }
639 656
640 // Render surface and left/right/bottom borders 657 // Render surface and left/right/bottom borders
641 if (current) { 658 if (current->type == C_VIEW) {
642 if (current->type == C_VIEW) { 659 render_view(output, damage, current, current_colors);
643 render_view(output, damage, current, current_colors); 660 } else {
644 } else { 661 render_container(output, damage, current,
645 render_container(output, damage, current, 662 parent_focused || current->current.focused);
646 parent_focused || current == focus);
647 }
648 } 663 }
649} 664}
650 665
@@ -657,26 +672,32 @@ static void render_container_stacked(struct sway_output *output,
657 if (!con->current.children->length) { 672 if (!con->current.children->length) {
658 return; 673 return;
659 } 674 }
660 struct sway_seat *seat = input_manager_current_seat(input_manager);
661 struct sway_container *focus = seat_get_focus(seat);
662 struct sway_container *current = seat_get_active_current_child(seat, con);
663 struct border_colors *current_colors = &config->border_colors.unfocused;
664 struct sway_container_state *pstate = &con->current; 675 struct sway_container_state *pstate = &con->current;
676 struct sway_container *current = pstate->focused_inactive_child;
677 struct border_colors *current_colors = &config->border_colors.unfocused;
678
679 size_t titlebar_height = container_titlebar_height();
665 680
666 // Render titles 681 // Render titles
667 for (int i = 0; i < con->current.children->length; ++i) { 682 for (int i = 0; i < pstate->children->length; ++i) {
668 struct sway_container *child = con->current.children->items[i]; 683 struct sway_container *child = pstate->children->items[i];
669 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 684 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
670 struct sway_container_state *cstate = &child->current; 685 struct sway_container_state *cstate = &child->current;
671 struct border_colors *colors; 686 struct border_colors *colors;
672 struct wlr_texture *title_texture; 687 struct wlr_texture *title_texture;
673 struct wlr_texture *marks_texture; 688 struct wlr_texture *marks_texture;
674 689 bool urgent = view ?
675 if (focus == child || parent_focused) { 690 view_is_urgent(view) : container_has_urgent_child(child);
691
692 if (urgent) {
693 colors = &config->border_colors.urgent;
694 title_texture = child->title_urgent;
695 marks_texture = view ? view->marks_urgent : NULL;
696 } else if (cstate->focused || parent_focused) {
676 colors = &config->border_colors.focused; 697 colors = &config->border_colors.focused;
677 title_texture = child->title_focused; 698 title_texture = child->title_focused;
678 marks_texture = view ? view->marks_focused : NULL; 699 marks_texture = view ? view->marks_focused : NULL;
679 } else if (child == current) { 700 } else if (child == pstate->focused_inactive_child) {
680 colors = &config->border_colors.focused_inactive; 701 colors = &config->border_colors.focused_inactive;
681 title_texture = child->title_focused_inactive; 702 title_texture = child->title_focused_inactive;
682 marks_texture = view ? view->marks_focused_inactive : NULL; 703 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -686,7 +707,7 @@ static void render_container_stacked(struct sway_output *output,
686 marks_texture = view ? view->marks_unfocused : NULL; 707 marks_texture = view ? view->marks_unfocused : NULL;
687 } 708 }
688 709
689 int y = pstate->swayc_y + container_titlebar_height() * i; 710 int y = cstate->swayc_y + titlebar_height * i;
690 render_titlebar(output, damage, child, cstate->swayc_x, y, 711 render_titlebar(output, damage, child, cstate->swayc_x, y,
691 cstate->swayc_width, colors, title_texture, marks_texture); 712 cstate->swayc_width, colors, title_texture, marks_texture);
692 713
@@ -696,13 +717,11 @@ static void render_container_stacked(struct sway_output *output,
696 } 717 }
697 718
698 // Render surface and left/right/bottom borders 719 // Render surface and left/right/bottom borders
699 if (current) { 720 if (current->type == C_VIEW) {
700 if (current->type == C_VIEW) { 721 render_view(output, damage, current, current_colors);
701 render_view(output, damage, current, current_colors); 722 } else {
702 } else { 723 render_container(output, damage, current,
703 render_container(output, damage, current, 724 parent_focused || current->current.focused);
704 parent_focused || current == focus);
705 }
706 } 725 }
707} 726}
708 727
@@ -730,13 +749,15 @@ static void render_floating_container(struct sway_output *soutput,
730 pixman_region32_t *damage, struct sway_container *con) { 749 pixman_region32_t *damage, struct sway_container *con) {
731 if (con->type == C_VIEW) { 750 if (con->type == C_VIEW) {
732 struct sway_view *view = con->sway_view; 751 struct sway_view *view = con->sway_view;
733 struct sway_seat *seat = input_manager_current_seat(input_manager);
734 struct sway_container *focus = seat_get_focus(seat);
735 struct border_colors *colors; 752 struct border_colors *colors;
736 struct wlr_texture *title_texture; 753 struct wlr_texture *title_texture;
737 struct wlr_texture *marks_texture; 754 struct wlr_texture *marks_texture;
738 755
739 if (focus == con) { 756 if (view_is_urgent(view)) {
757 colors = &config->border_colors.urgent;
758 title_texture = con->title_urgent;
759 marks_texture = view->marks_urgent;
760 } else if (con->current.focused) {
740 colors = &config->border_colors.focused; 761 colors = &config->border_colors.focused;
741 title_texture = con->title_focused; 762 title_texture = con->title_focused;
742 marks_texture = view->marks_focused; 763 marks_texture = view->marks_focused;
@@ -746,12 +767,14 @@ static void render_floating_container(struct sway_output *soutput,
746 marks_texture = view->marks_unfocused; 767 marks_texture = view->marks_unfocused;
747 } 768 }
748 769
749 if (con->current.border == B_NORMAL) { 770 if (!view->using_csd) {
750 render_titlebar(soutput, damage, con, con->current.swayc_x, 771 if (con->current.border == B_NORMAL) {
751 con->current.swayc_y, con->current.swayc_width, colors, 772 render_titlebar(soutput, damage, con, con->current.swayc_x,
752 title_texture, marks_texture); 773 con->current.swayc_y, con->current.swayc_width, colors,
753 } else if (con->current.border != B_NONE) { 774 title_texture, marks_texture);
754 render_top_border(soutput, damage, con, colors); 775 } else if (con->current.border != B_NONE) {
776 render_top_border(soutput, damage, con, colors);
777 }
755 } 778 }
756 render_view(soutput, damage, con, colors); 779 render_view(soutput, damage, con, colors);
757 } else { 780 } else {
@@ -779,6 +802,8 @@ static void render_floating(struct sway_output *soutput,
779 } 802 }
780} 803}
781 804
805const char *damage_debug = NULL;
806
782void output_render(struct sway_output *output, struct timespec *when, 807void output_render(struct sway_output *output, struct timespec *when,
783 pixman_region32_t *damage) { 808 pixman_region32_t *damage) {
784 struct wlr_output *wlr_output = output->wlr_output; 809 struct wlr_output *wlr_output = output->wlr_output;
@@ -798,7 +823,6 @@ void output_render(struct sway_output *output, struct timespec *when,
798 goto renderer_end; 823 goto renderer_end;
799 } 824 }
800 825
801 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
802 if (damage_debug != NULL) { 826 if (damage_debug != NULL) {
803 if (strcmp(damage_debug, "highlight") == 0) { 827 if (strcmp(damage_debug, "highlight") == 0) {
804 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); 828 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
@@ -837,7 +861,11 @@ void output_render(struct sway_output *output, struct timespec *when,
837 } 861 }
838 862
839 // TODO: handle views smaller than the output 863 // TODO: handle views smaller than the output
840 render_view_surfaces(fullscreen_view, output, damage, 1.0f); 864 if (fullscreen_view->swayc->instructions->length) {
865 render_saved_view(fullscreen_view, output, damage, 1.0f);
866 } else {
867 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
868 }
841 869
842 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { 870 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
843 render_unmanaged(output, damage, 871 render_unmanaged(output, damage,
@@ -858,9 +886,7 @@ void output_render(struct sway_output *output, struct timespec *when,
858 render_layer(output, damage, 886 render_layer(output, damage,
859 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); 887 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
860 888
861 struct sway_seat *seat = input_manager_current_seat(input_manager); 889 render_container(output, damage, workspace, workspace->current.focused);
862 struct sway_container *focus = seat_get_focus(seat);
863 render_container(output, damage, workspace, focus == workspace);
864 render_floating(output, damage); 890 render_floating(output, damage);
865 891
866 render_unmanaged(output, damage, 892 render_unmanaged(output, damage,
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 179af617..fcfb0b51 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -19,14 +19,14 @@
19 * How long we should wait for views to respond to the configure before giving 19 * How long we should wait for views to respond to the configure before giving
20 * up and applying the transaction anyway. 20 * up and applying the transaction anyway.
21 */ 21 */
22#define TIMEOUT_MS 200 22int txn_timeout_ms = 200;
23 23
24/** 24/**
25 * If enabled, sway will always wait for the transaction timeout before 25 * If enabled, sway will always wait for the transaction timeout before
26 * applying it, rather than applying it when the views are ready. This allows us 26 * applying it, rather than applying it when the views are ready. This allows us
27 * to observe the rendered state while a transaction is in progress. 27 * to observe the rendered state while a transaction is in progress.
28 */ 28 */
29#define TRANSACTION_DEBUG false 29bool txn_debug = false;
30 30
31struct sway_transaction { 31struct sway_transaction {
32 struct wl_event_source *timer; 32 struct wl_event_source *timer;
@@ -47,7 +47,7 @@ struct sway_transaction_instruction {
47 bool ready; 47 bool ready;
48}; 48};
49 49
50struct sway_transaction *transaction_create() { 50static struct sway_transaction *transaction_create() {
51 struct sway_transaction *transaction = 51 struct sway_transaction *transaction =
52 calloc(1, sizeof(struct sway_transaction)); 52 calloc(1, sizeof(struct sway_transaction));
53 transaction->instructions = create_list(); 53 transaction->instructions = create_list();
@@ -139,25 +139,18 @@ static void copy_pending_state(struct sway_container *container,
139 state->children = create_list(); 139 state->children = create_list();
140 list_cat(state->children, container->children); 140 list_cat(state->children, container->children);
141 } 141 }
142}
143 142
144static bool transaction_has_container(struct sway_transaction *transaction, 143 struct sway_seat *seat = input_manager_current_seat(input_manager);
145 struct sway_container *container) { 144 state->focused = seat_get_focus(seat) == container;
146 for (int i = 0; i < transaction->instructions->length; ++i) { 145
147 struct sway_transaction_instruction *instruction = 146 if (container->type != C_VIEW) {
148 transaction->instructions->items[i]; 147 state->focused_inactive_child =
149 if (instruction->container == container) { 148 seat_get_active_child(seat, container);
150 return true;
151 }
152 } 149 }
153 return false;
154} 150}
155 151
156void transaction_add_container(struct sway_transaction *transaction, 152static void transaction_add_container(struct sway_transaction *transaction,
157 struct sway_container *container) { 153 struct sway_container *container) {
158 if (transaction_has_container(transaction, container)) {
159 return;
160 }
161 struct sway_transaction_instruction *instruction = 154 struct sway_transaction_instruction *instruction =
162 calloc(1, sizeof(struct sway_transaction_instruction)); 155 calloc(1, sizeof(struct sway_transaction_instruction));
163 instruction->transaction = transaction; 156 instruction->transaction = transaction;
@@ -175,7 +168,7 @@ void transaction_add_container(struct sway_transaction *transaction,
175 * Apply a transaction to the "current" state of the tree. 168 * Apply a transaction to the "current" state of the tree.
176 */ 169 */
177static void transaction_apply(struct sway_transaction *transaction) { 170static void transaction_apply(struct sway_transaction *transaction) {
178 wlr_log(L_DEBUG, "Applying transaction %p", transaction); 171 wlr_log(WLR_DEBUG, "Applying transaction %p", transaction);
179 if (server.debug_txn_timings) { 172 if (server.debug_txn_timings) {
180 struct timespec now; 173 struct timespec now;
181 clock_gettime(CLOCK_MONOTONIC, &now); 174 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -186,7 +179,7 @@ static void transaction_apply(struct sway_transaction *transaction) {
186 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + 179 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +
187 (now.tv_nsec - commit->tv_nsec) / 1000000.0; 180 (now.tv_nsec - commit->tv_nsec) / 1000000.0;
188 float ms_total = ms_arranging + ms_waiting; 181 float ms_total = ms_arranging + ms_waiting;
189 wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " 182 wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
190 "%.1fms total (%.1f frames if 60Hz)", transaction, 183 "%.1fms total (%.1f frames if 60Hz)", transaction,
191 ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60)); 184 ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60));
192 } 185 }
@@ -210,10 +203,12 @@ static void transaction_apply(struct sway_transaction *transaction) {
210 .width = instruction->state.swayc_width, 203 .width = instruction->state.swayc_width,
211 .height = instruction->state.swayc_height, 204 .height = instruction->state.swayc_height,
212 }; 205 };
213 for (int j = 0; j < root_container.children->length; ++j) { 206 for (int j = 0; j < root_container.current.children->length; ++j) {
214 struct sway_container *output = root_container.children->items[j]; 207 struct sway_container *output = root_container.current.children->items[j];
215 output_damage_box(output->sway_output, &old_box); 208 if (output->sway_output) {
216 output_damage_box(output->sway_output, &new_box); 209 output_damage_box(output->sway_output, &old_box);
210 output_damage_box(output->sway_output, &new_box);
211 }
217 } 212 }
218 213
219 // There are separate children lists for each instruction state, the 214 // There are separate children lists for each instruction state, the
@@ -251,7 +246,7 @@ static void transaction_progress_queue() {
251 246
252static int handle_timeout(void *data) { 247static int handle_timeout(void *data) {
253 struct sway_transaction *transaction = data; 248 struct sway_transaction *transaction = data;
254 wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", 249 wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)",
255 transaction, transaction->num_waiting); 250 transaction, transaction->num_waiting);
256 transaction->num_waiting = 0; 251 transaction->num_waiting = 0;
257 transaction_progress_queue(); 252 transaction_progress_queue();
@@ -285,8 +280,8 @@ static bool should_configure(struct sway_container *con,
285 return true; 280 return true;
286} 281}
287 282
288void transaction_commit(struct sway_transaction *transaction) { 283static void transaction_commit(struct sway_transaction *transaction) {
289 wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", 284 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
290 transaction, transaction->instructions->length); 285 transaction, transaction->instructions->length);
291 transaction->num_waiting = 0; 286 transaction->num_waiting = 0;
292 for (int i = 0; i < transaction->instructions->length; ++i) { 287 for (int i = 0; i < transaction->instructions->length; ++i) {
@@ -319,7 +314,7 @@ void transaction_commit(struct sway_transaction *transaction) {
319 } else { 314 } else {
320 // There are no other transactions in progress, and this one has nothing 315 // There are no other transactions in progress, and this one has nothing
321 // to wait for, so we can skip the queue. 316 // to wait for, so we can skip the queue.
322 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); 317 wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction);
323 transaction_apply(transaction); 318 transaction_apply(transaction);
324 transaction_destroy(transaction); 319 transaction_destroy(transaction);
325 idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); 320 idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
@@ -330,7 +325,7 @@ void transaction_commit(struct sway_transaction *transaction) {
330 // Set up a timer which the views must respond within 325 // Set up a timer which the views must respond within
331 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, 326 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
332 handle_timeout, transaction); 327 handle_timeout, transaction);
333 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); 328 wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
334 } 329 }
335 330
336 // The debug tree shows the pending/live tree. Here is a good place to 331 // The debug tree shows the pending/live tree. Here is a good place to
@@ -350,7 +345,7 @@ static void set_instruction_ready(
350 struct timespec *start = &transaction->commit_time; 345 struct timespec *start = &transaction->commit_time;
351 float ms = (now.tv_sec - start->tv_sec) * 1000 + 346 float ms = (now.tv_sec - start->tv_sec) * 1000 +
352 (now.tv_nsec - start->tv_nsec) / 1000000.0; 347 (now.tv_nsec - start->tv_nsec) / 1000000.0;
353 wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", 348 wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
354 transaction, 349 transaction,
355 transaction->num_configures - transaction->num_waiting + 1, 350 transaction->num_configures - transaction->num_waiting + 1,
356 transaction->num_configures, ms, 351 transaction->num_configures, ms,
@@ -361,11 +356,11 @@ static void set_instruction_ready(
361 // If all views are ready, apply the transaction. 356 // If all views are ready, apply the transaction.
362 // If the transaction has timed out then its num_waiting will be 0 already. 357 // If the transaction has timed out then its num_waiting will be 0 already.
363 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { 358 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
364#if !TRANSACTION_DEBUG 359 if (!txn_debug) {
365 wlr_log(L_DEBUG, "Transaction %p is ready", transaction); 360 wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
366 wl_event_source_timer_update(transaction->timer, 0); 361 wl_event_source_timer_update(transaction->timer, 0);
367 transaction_progress_queue(); 362 transaction_progress_queue();
368#endif 363 }
369 } 364 }
370} 365}
371 366
@@ -418,3 +413,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
418 *height = instruction->saved_buffer_height; 413 *height = instruction->saved_buffer_height;
419 return instruction->saved_buffer->texture; 414 return instruction->saved_buffer->texture;
420} 415}
416
417void transaction_commit_dirty(void) {
418 if (!server.dirty_containers->length) {
419 return;
420 }
421 struct sway_transaction *transaction = transaction_create();
422 for (int i = 0; i < server.dirty_containers->length; ++i) {
423 struct sway_container *container = server.dirty_containers->items[i];
424 transaction_add_container(transaction, container);
425 container->dirty = false;
426 }
427 server.dirty_containers->length = 0;
428 transaction_commit(transaction);
429}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index ac35a8d1..98c16faf 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -45,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
45 view_child_destroy(&popup->child); 45 view_child_destroy(&popup->child);
46} 46}
47 47
48static void popup_unconstrain(struct sway_xdg_popup *popup) {
49 struct sway_view *view = popup->child.view;
50 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
51
52 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
53
54 // the output box expressed in the coordinate system of the toplevel parent
55 // of the popup
56 struct wlr_box output_toplevel_sx_box = {
57 .x = output->x - view->x,
58 .y = output->y - view->y,
59 .width = output->width,
60 .height = output->height,
61 };
62
63 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
64}
65
48static struct sway_xdg_popup *popup_create( 66static struct sway_xdg_popup *popup_create(
49 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { 67 struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {
50 struct wlr_xdg_surface *xdg_surface = wlr_popup->base; 68 struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
@@ -55,12 +73,15 @@ static struct sway_xdg_popup *popup_create(
55 return NULL; 73 return NULL;
56 } 74 }
57 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 75 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
76 popup->wlr_xdg_surface = xdg_surface;
58 77
59 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 78 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
60 popup->new_popup.notify = popup_handle_new_popup; 79 popup->new_popup.notify = popup_handle_new_popup;
61 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 80 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
62 popup->destroy.notify = popup_handle_destroy; 81 popup->destroy.notify = popup_handle_destroy;
63 82
83 popup_unconstrain(popup);
84
64 return popup; 85 return popup;
65} 86}
66 87
@@ -223,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
223 view_set_fullscreen(view, e->fullscreen); 244 view_set_fullscreen(view, e->fullscreen);
224 245
225 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 246 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
226 arrange_and_commit(output); 247 arrange_windows(output);
248 transaction_commit_dirty();
227} 249}
228 250
229static void handle_unmap(struct wl_listener *listener, void *data) { 251static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -260,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
260 if (xdg_surface->toplevel->client_pending.fullscreen) { 282 if (xdg_surface->toplevel->client_pending.fullscreen) {
261 view_set_fullscreen(view, true); 283 view_set_fullscreen(view, true);
262 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
263 arrange_and_commit(ws); 285 arrange_windows(ws);
264 } else { 286 } else {
265 arrange_and_commit(view->swayc->parent); 287 arrange_windows(view->swayc->parent);
266 } 288 }
289 transaction_commit_dirty();
267 290
268 xdg_shell_view->commit.notify = handle_commit; 291 xdg_shell_view->commit.notify = handle_commit;
269 wl_signal_add(&xdg_surface->surface->events.commit, 292 wl_signal_add(&xdg_surface->surface->events.commit,
@@ -304,11 +327,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
304 struct wlr_xdg_surface *xdg_surface = data; 327 struct wlr_xdg_surface *xdg_surface = data;
305 328
306 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { 329 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
307 wlr_log(L_DEBUG, "New xdg_shell popup"); 330 wlr_log(WLR_DEBUG, "New xdg_shell popup");
308 return; 331 return;
309 } 332 }
310 333
311 wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", 334 wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
312 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 335 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
313 wlr_xdg_surface_ping(xdg_surface); 336 wlr_xdg_surface_ping(xdg_surface);
314 337
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 56bbb244..4d76f0a7 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -44,6 +44,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
44 view_child_destroy(&popup->child); 44 view_child_destroy(&popup->child);
45} 45}
46 46
47static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
48 struct sway_view *view = popup->child.view;
49 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
50
51 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
52
53 // the output box expressed in the coordinate system of the toplevel parent
54 // of the popup
55 struct wlr_box output_toplevel_sx_box = {
56 .x = output->x - view->x,
57 .y = output->y - view->y,
58 .width = output->width,
59 .height = output->height,
60 };
61
62 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
63}
64
47static struct sway_xdg_popup_v6 *popup_create( 65static struct sway_xdg_popup_v6 *popup_create(
48 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) { 66 struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
49 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base; 67 struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
@@ -54,12 +72,15 @@ static struct sway_xdg_popup_v6 *popup_create(
54 return NULL; 72 return NULL;
55 } 73 }
56 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); 74 view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
75 popup->wlr_xdg_surface_v6 = xdg_surface;
57 76
58 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); 77 wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
59 popup->new_popup.notify = popup_handle_new_popup; 78 popup->new_popup.notify = popup_handle_new_popup;
60 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); 79 wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
61 popup->destroy.notify = popup_handle_destroy; 80 popup->destroy.notify = popup_handle_destroy;
62 81
82 popup_unconstrain(popup);
83
63 return popup; 84 return popup;
64} 85}
65 86
@@ -218,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
218 view_set_fullscreen(view, e->fullscreen); 239 view_set_fullscreen(view, e->fullscreen);
219 240
220 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 241 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
221 arrange_and_commit(output); 242 arrange_windows(output);
243 transaction_commit_dirty();
222} 244}
223 245
224static void handle_unmap(struct wl_listener *listener, void *data) { 246static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -255,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
255 if (xdg_surface->toplevel->client_pending.fullscreen) { 277 if (xdg_surface->toplevel->client_pending.fullscreen) {
256 view_set_fullscreen(view, true); 278 view_set_fullscreen(view, true);
257 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 279 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
258 arrange_and_commit(ws); 280 arrange_windows(ws);
259 } else { 281 } else {
260 arrange_and_commit(view->swayc->parent); 282 arrange_windows(view->swayc->parent);
261 } 283 }
284 transaction_commit_dirty();
262 285
263 xdg_shell_v6_view->commit.notify = handle_commit; 286 xdg_shell_v6_view->commit.notify = handle_commit;
264 wl_signal_add(&xdg_surface->surface->events.commit, 287 wl_signal_add(&xdg_surface->surface->events.commit,
@@ -295,11 +318,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
295 struct wlr_xdg_surface_v6 *xdg_surface = data; 318 struct wlr_xdg_surface_v6 *xdg_surface = data;
296 319
297 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { 320 if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
298 wlr_log(L_DEBUG, "New xdg_shell_v6 popup"); 321 wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup");
299 return; 322 return;
300 } 323 }
301 324
302 wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'", 325 wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
303 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); 326 xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
304 wlr_xdg_surface_v6_ping(xdg_surface); 327 wlr_xdg_surface_v6_ping(xdg_surface);
305 328
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index b2874cfe..9df7977d 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -119,7 +119,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
119 struct sway_xwayland_unmanaged *surface = 119 struct sway_xwayland_unmanaged *surface =
120 calloc(1, sizeof(struct sway_xwayland_unmanaged)); 120 calloc(1, sizeof(struct sway_xwayland_unmanaged));
121 if (surface == NULL) { 121 if (surface == NULL) {
122 wlr_log(L_ERROR, "Allocation failed"); 122 wlr_log(WLR_ERROR, "Allocation failed");
123 return NULL; 123 return NULL;
124 } 124 }
125 125
@@ -246,6 +246,14 @@ static bool wants_floating(struct sway_view *view) {
246 return false; 246 return false;
247} 247}
248 248
249static bool has_client_side_decorations(struct sway_view *view) {
250 if (xwayland_view_from_view(view) == NULL) {
251 return false;
252 }
253 struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
254 return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL;
255}
256
249static void _close(struct sway_view *view) { 257static void _close(struct sway_view *view) {
250 if (xwayland_view_from_view(view) == NULL) { 258 if (xwayland_view_from_view(view) == NULL) {
251 return; 259 return;
@@ -269,6 +277,7 @@ static const struct sway_view_impl view_impl = {
269 .set_tiled = set_tiled, 277 .set_tiled = set_tiled,
270 .set_fullscreen = set_fullscreen, 278 .set_fullscreen = set_fullscreen,
271 .wants_floating = wants_floating, 279 .wants_floating = wants_floating,
280 .has_client_side_decorations = has_client_side_decorations,
272 .close = _close, 281 .close = _close,
273 .destroy = destroy, 282 .destroy = destroy,
274}; 283};
@@ -288,6 +297,10 @@ static void handle_commit(struct wl_listener *listener, void *data) {
288 } 297 }
289 298
290 view_damage_from(view); 299 view_damage_from(view);
300
301 if (view->allow_request_urgent) {
302 view_set_urgent(view, (bool)xsurface->hints_urgency);
303 }
291} 304}
292 305
293static void handle_unmap(struct wl_listener *listener, void *data) { 306static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -324,10 +337,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
324 if (xsurface->fullscreen) { 337 if (xsurface->fullscreen) {
325 view_set_fullscreen(view, true); 338 view_set_fullscreen(view, true);
326 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 339 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
327 arrange_and_commit(ws); 340 arrange_windows(ws);
328 } else { 341 } else {
329 arrange_and_commit(view->swayc->parent); 342 arrange_windows(view->swayc->parent);
330 } 343 }
344 transaction_commit_dirty();
331} 345}
332 346
333static void handle_destroy(struct wl_listener *listener, void *data) { 347static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -383,7 +397,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
383 view_set_fullscreen(view, xsurface->fullscreen); 397 view_set_fullscreen(view, xsurface->fullscreen);
384 398
385 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 399 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
386 arrange_and_commit(output); 400 arrange_windows(output);
401 transaction_commit_dirty();
387} 402}
388 403
389static void handle_set_title(struct wl_listener *listener, void *data) { 404static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -432,12 +447,12 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
432 447
433 if (wlr_xwayland_surface_is_unmanaged(xsurface) || 448 if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
434 xsurface->override_redirect) { 449 xsurface->override_redirect) {
435 wlr_log(L_DEBUG, "New xwayland unmanaged surface"); 450 wlr_log(WLR_DEBUG, "New xwayland unmanaged surface");
436 create_unmanaged(xsurface); 451 create_unmanaged(xsurface);
437 return; 452 return;
438 } 453 }
439 454
440 wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'", 455 wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'",
441 xsurface->title, xsurface->class); 456 xsurface->title, xsurface->class);
442 457
443 struct sway_xwayland_view *xwayland_view = 458 struct sway_xwayland_view *xwayland_view =
@@ -490,7 +505,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
490 xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); 505 xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL);
491 int err = xcb_connection_has_error(xcb_conn); 506 int err = xcb_connection_has_error(xcb_conn);
492 if (err) { 507 if (err) {
493 wlr_log(L_ERROR, "XCB connect failed: %d", err); 508 wlr_log(WLR_ERROR, "XCB connect failed: %d", err);
494 return; 509 return;
495 } 510 }
496 511
@@ -509,7 +524,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
509 free(reply); 524 free(reply);
510 525
511 if (error != NULL) { 526 if (error != NULL) {
512 wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", 527 wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d",
513 atom_map[i], error->error_code); 528 atom_map[i], error->error_code);
514 free(error); 529 free(error);
515 break; 530 break;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index a2f11557..7a9f3ed7 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -10,6 +10,7 @@
10#include <wlr/types/wlr_idle.h> 10#include <wlr/types/wlr_idle.h>
11#include "list.h" 11#include "list.h"
12#include "log.h" 12#include "log.h"
13#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
14#include "sway/layers.h" 15#include "sway/layers.h"
15#include "sway/output.h" 16#include "sway/output.h"
@@ -219,6 +220,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
219 struct sway_drag_icon *drag_icon = wlr_drag_icon->data; 220 struct sway_drag_icon *drag_icon = wlr_drag_icon->data;
220 drag_icon_update_position(drag_icon); 221 drag_icon_update_position(drag_icon);
221 } 222 }
223 transaction_commit_dirty();
222} 224}
223 225
224static void handle_cursor_motion(struct wl_listener *listener, void *data) { 226static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@@ -278,6 +280,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
278 280
279 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, 281 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
280 time_msec, button, state); 282 time_msec, button, state);
283 transaction_commit_dirty();
281} 284}
282 285
283static void handle_cursor_button(struct wl_listener *listener, void *data) { 286static void handle_cursor_button(struct wl_listener *listener, void *data) {
@@ -474,7 +477,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
474 // TODO: check cursor mode 477 // TODO: check cursor mode
475 if (focused_client == NULL || 478 if (focused_client == NULL ||
476 event->seat_client->client != focused_client) { 479 event->seat_client->client != focused_client) {
477 wlr_log(L_DEBUG, "denying request to set cursor from unfocused client"); 480 wlr_log(WLR_DEBUG, "denying request to set cursor from unfocused client");
478 return; 481 return;
479 } 482 }
480 483
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 98f7d7cf..0b7cb766 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -61,7 +61,7 @@ static char *get_device_identifier(struct wlr_input_device *device) {
61 int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; 61 int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
62 char *identifier = malloc(len); 62 char *identifier = malloc(len);
63 if (!identifier) { 63 if (!identifier) {
64 wlr_log(L_ERROR, "Unable to allocate unique input device name"); 64 wlr_log(WLR_ERROR, "Unable to allocate unique input device name");
65 return NULL; 65 return NULL;
66 } 66 }
67 67
@@ -104,77 +104,89 @@ static void input_manager_libinput_config_pointer(
104 } 104 }
105 105
106 libinput_device = wlr_libinput_get_device_handle(wlr_device); 106 libinput_device = wlr_libinput_get_device_handle(wlr_device);
107 wlr_log(L_DEBUG, "input_manager_libinput_config_pointer(%s)", 107 wlr_log(WLR_DEBUG, "input_manager_libinput_config_pointer(%s)",
108 ic->identifier); 108 ic->identifier);
109 109
110 if (ic->accel_profile != INT_MIN) { 110 if (ic->accel_profile != INT_MIN) {
111 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)", 111 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_profile(%d)",
112 ic->identifier, ic->accel_profile); 112 ic->identifier, ic->accel_profile);
113 libinput_device_config_accel_set_profile(libinput_device, 113 libinput_device_config_accel_set_profile(libinput_device,
114 ic->accel_profile); 114 ic->accel_profile);
115 } 115 }
116 if (ic->click_method != INT_MIN) { 116 if (ic->click_method != INT_MIN) {
117 wlr_log(L_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)", 117 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) click_set_method(%d)",
118 ic->identifier, ic->click_method); 118 ic->identifier, ic->click_method);
119 libinput_device_config_click_set_method(libinput_device, 119 libinput_device_config_click_set_method(libinput_device,
120 ic->click_method); 120 ic->click_method);
121 } 121 }
122 if (ic->drag_lock != INT_MIN) { 122 if (ic->drag_lock != INT_MIN) {
123 wlr_log(L_DEBUG, 123 wlr_log(WLR_DEBUG,
124 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 124 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
125 ic->identifier, ic->click_method); 125 ic->identifier, ic->click_method);
126 libinput_device_config_tap_set_drag_lock_enabled(libinput_device, 126 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
127 ic->drag_lock); 127 ic->drag_lock);
128 } 128 }
129 if (ic->dwt != INT_MIN) { 129 if (ic->dwt != INT_MIN) {
130 wlr_log(L_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)", 130 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) dwt_set_enabled(%d)",
131 ic->identifier, ic->dwt); 131 ic->identifier, ic->dwt);
132 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt); 132 libinput_device_config_dwt_set_enabled(libinput_device, ic->dwt);
133 } 133 }
134 if (ic->left_handed != INT_MIN) { 134 if (ic->left_handed != INT_MIN) {
135 wlr_log(L_DEBUG, 135 wlr_log(WLR_DEBUG,
136 "libinput_config_pointer(%s) left_handed_set_enabled(%d)", 136 "libinput_config_pointer(%s) left_handed_set_enabled(%d)",
137 ic->identifier, ic->left_handed); 137 ic->identifier, ic->left_handed);
138 libinput_device_config_left_handed_set(libinput_device, 138 libinput_device_config_left_handed_set(libinput_device,
139 ic->left_handed); 139 ic->left_handed);
140 } 140 }
141 if (ic->middle_emulation != INT_MIN) { 141 if (ic->middle_emulation != INT_MIN) {
142 wlr_log(L_DEBUG, 142 wlr_log(WLR_DEBUG,
143 "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)", 143 "libinput_config_pointer(%s) middle_emulation_set_enabled(%d)",
144 ic->identifier, ic->middle_emulation); 144 ic->identifier, ic->middle_emulation);
145 libinput_device_config_middle_emulation_set_enabled(libinput_device, 145 libinput_device_config_middle_emulation_set_enabled(libinput_device,
146 ic->middle_emulation); 146 ic->middle_emulation);
147 } 147 }
148 if (ic->natural_scroll != INT_MIN) { 148 if (ic->natural_scroll != INT_MIN) {
149 wlr_log(L_DEBUG, 149 wlr_log(WLR_DEBUG,
150 "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)", 150 "libinput_config_pointer(%s) natural_scroll_set_enabled(%d)",
151 ic->identifier, ic->natural_scroll); 151 ic->identifier, ic->natural_scroll);
152 libinput_device_config_scroll_set_natural_scroll_enabled( 152 libinput_device_config_scroll_set_natural_scroll_enabled(
153 libinput_device, ic->natural_scroll); 153 libinput_device, ic->natural_scroll);
154 } 154 }
155 if (ic->pointer_accel != FLT_MIN) { 155 if (ic->pointer_accel != FLT_MIN) {
156 wlr_log(L_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)", 156 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) accel_set_speed(%f)",
157 ic->identifier, ic->pointer_accel); 157 ic->identifier, ic->pointer_accel);
158 libinput_device_config_accel_set_speed(libinput_device, 158 libinput_device_config_accel_set_speed(libinput_device,
159 ic->pointer_accel); 159 ic->pointer_accel);
160 } 160 }
161 if (ic->scroll_button != INT_MIN) {
162 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_button(%d)",
163 ic->identifier, ic->scroll_button);
164 libinput_device_config_scroll_set_button(libinput_device,
165 ic->scroll_button);
166 }
161 if (ic->scroll_method != INT_MIN) { 167 if (ic->scroll_method != INT_MIN) {
162 wlr_log(L_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)", 168 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) scroll_set_method(%d)",
163 ic->identifier, ic->scroll_method); 169 ic->identifier, ic->scroll_method);
164 libinput_device_config_scroll_set_method(libinput_device, 170 libinput_device_config_scroll_set_method(libinput_device,
165 ic->scroll_method); 171 ic->scroll_method);
166 } 172 }
167 if (ic->send_events != INT_MIN) { 173 if (ic->send_events != INT_MIN) {
168 wlr_log(L_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)", 174 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) send_events_set_mode(%d)",
169 ic->identifier, ic->send_events); 175 ic->identifier, ic->send_events);
170 libinput_device_config_send_events_set_mode(libinput_device, 176 libinput_device_config_send_events_set_mode(libinput_device,
171 ic->send_events); 177 ic->send_events);
172 } 178 }
173 if (ic->tap != INT_MIN) { 179 if (ic->tap != INT_MIN) {
174 wlr_log(L_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)", 180 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_enabled(%d)",
175 ic->identifier, ic->tap); 181 ic->identifier, ic->tap);
176 libinput_device_config_tap_set_enabled(libinput_device, ic->tap); 182 libinput_device_config_tap_set_enabled(libinput_device, ic->tap);
177 } 183 }
184 if (ic->tap_button_map != INT_MIN) {
185 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)",
186 ic->identifier, ic->tap);
187 libinput_device_config_tap_set_button_map(libinput_device,
188 ic->tap_button_map);
189 }
178} 190}
179 191
180static void handle_device_destroy(struct wl_listener *listener, void *data) { 192static void handle_device_destroy(struct wl_listener *listener, void *data) {
@@ -187,7 +199,7 @@ static void handle_device_destroy(struct wl_listener *listener, void *data) {
187 return; 199 return;
188 } 200 }
189 201
190 wlr_log(L_DEBUG, "removing device: '%s'", 202 wlr_log(WLR_DEBUG, "removing device: '%s'",
191 input_device->identifier); 203 input_device->identifier);
192 204
193 struct sway_seat *seat = NULL; 205 struct sway_seat *seat = NULL;
@@ -217,7 +229,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
217 input_device->identifier = get_device_identifier(device); 229 input_device->identifier = get_device_identifier(device);
218 wl_list_insert(&input->devices, &input_device->link); 230 wl_list_insert(&input->devices, &input_device->link);
219 231
220 wlr_log(L_DEBUG, "adding device: '%s'", 232 wlr_log(WLR_DEBUG, "adding device: '%s'",
221 input_device->identifier); 233 input_device->identifier);
222 234
223 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 235 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
@@ -229,7 +241,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
229 241
230 struct sway_seat *seat = NULL; 242 struct sway_seat *seat = NULL;
231 if (!input_has_seat_configuration(input)) { 243 if (!input_has_seat_configuration(input)) {
232 wlr_log(L_DEBUG, "no seat configuration, using default seat"); 244 wlr_log(WLR_DEBUG, "no seat configuration, using default seat");
233 seat = input_manager_get_seat(input, default_seat); 245 seat = input_manager_get_seat(input, default_seat);
234 seat_add_device(seat, input_device); 246 seat_add_device(seat, input_device);
235 return; 247 return;
@@ -259,7 +271,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
259 } 271 }
260 272
261 if (!added) { 273 if (!added) {
262 wlr_log(L_DEBUG, 274 wlr_log(WLR_DEBUG,
263 "device '%s' is not configured on any seats", 275 "device '%s' is not configured on any seats",
264 input_device->identifier); 276 input_device->identifier);
265 } 277 }
@@ -282,7 +294,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
282 seat_set_exclusive_client(seat, NULL); 294 seat_set_exclusive_client(seat, NULL);
283 struct sway_container *previous = seat_get_focus(seat); 295 struct sway_container *previous = seat_get_focus(seat);
284 if (previous) { 296 if (previous) {
285 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, 297 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
286 container_type_to_str(previous->type), previous->name); 298 container_type_to_str(previous->type), previous->name);
287 // Hack to get seat to re-focus the return value of get_focus 299 // Hack to get seat to re-focus the return value of get_focus
288 seat_set_focus(seat, previous->parent); 300 seat_set_focus(seat, previous->parent);
@@ -359,7 +371,7 @@ void input_manager_apply_input_config(struct sway_input_manager *input,
359 371
360void input_manager_apply_seat_config(struct sway_input_manager *input, 372void input_manager_apply_seat_config(struct sway_input_manager *input,
361 struct seat_config *seat_config) { 373 struct seat_config *seat_config) {
362 wlr_log(L_DEBUG, "applying new seat config for seat %s", 374 wlr_log(WLR_DEBUG, "applying new seat config for seat %s",
363 seat_config->name); 375 seat_config->name);
364 struct sway_seat *seat = input_manager_get_seat(input, seat_config->name); 376 struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
365 if (!seat) { 377 if (!seat) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 182536de..ede38519 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -3,6 +3,7 @@
3#include <wlr/backend/multi.h> 3#include <wlr/backend/multi.h>
4#include <wlr/backend/session.h> 4#include <wlr/backend/session.h>
5#include <wlr/types/wlr_idle.h> 5#include <wlr/types/wlr_idle.h>
6#include "sway/desktop/transaction.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/input/keyboard.h" 8#include "sway/input/keyboard.h"
8#include "sway/input/input-manager.h" 9#include "sway/input/input-manager.h"
@@ -108,7 +109,7 @@ static void get_active_binding(const struct sway_shortcut_state *state,
108 } 109 }
109 110
110 if (*current_binding && *current_binding != binding) { 111 if (*current_binding && *current_binding != binding) {
111 wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", 112 wlr_log(WLR_DEBUG, "encountered duplicate bindings %d and %d",
112 (*current_binding)->order, binding->order); 113 (*current_binding)->order, binding->order);
113 } else { 114 } else {
114 *current_binding = binding; 115 *current_binding = binding;
@@ -122,12 +123,13 @@ static void get_active_binding(const struct sway_shortcut_state *state,
122 */ 123 */
123static void keyboard_execute_command(struct sway_keyboard *keyboard, 124static void keyboard_execute_command(struct sway_keyboard *keyboard,
124 struct sway_binding *binding) { 125 struct sway_binding *binding) {
125 wlr_log(L_DEBUG, "running command for binding: %s", 126 wlr_log(WLR_DEBUG, "running command for binding: %s",
126 binding->command); 127 binding->command);
127 config->handler_context.seat = keyboard->seat_device->sway_seat; 128 config->handler_context.seat = keyboard->seat_device->sway_seat;
128 struct cmd_results *results = execute_command(binding->command, NULL); 129 struct cmd_results *results = execute_command(binding->command, NULL);
130 transaction_commit_dirty();
129 if (results->status != CMD_SUCCESS) { 131 if (results->status != CMD_SUCCESS) {
130 wlr_log(L_DEBUG, "could not run command for binding: %s (%s)", 132 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
131 binding->command, results->error); 133 binding->command, results->error);
132 } 134 }
133 free_cmd_results(results); 135 free_cmd_results(results);
@@ -386,7 +388,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
386 xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); 388 xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
387 389
388 if (!keymap) { 390 if (!keymap) {
389 wlr_log(L_DEBUG, "cannot configure keyboard: keymap does not exist"); 391 wlr_log(WLR_DEBUG, "cannot configure keyboard: keymap does not exist");
390 xkb_context_unref(context); 392 xkb_context_unref(context);
391 return; 393 return;
392 } 394 }
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 6c5abcd8..12b1fab5 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -75,7 +75,7 @@ static void seat_send_activate(struct sway_container *con,
75 struct sway_seat *seat) { 75 struct sway_seat *seat) {
76 if (con->type == C_VIEW) { 76 if (con->type == C_VIEW) {
77 if (!seat_is_input_allowed(seat, con->sway_view->surface)) { 77 if (!seat_is_input_allowed(seat, con->sway_view->surface)) {
78 wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); 78 wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited");
79 return; 79 return;
80 } 80 }
81 view_set_activated(con->sway_view, true); 81 view_set_activated(con->sway_view, true);
@@ -219,7 +219,7 @@ static struct sway_seat_container *seat_container_from_container(
219 219
220 seat_con = calloc(1, sizeof(struct sway_seat_container)); 220 seat_con = calloc(1, sizeof(struct sway_seat_container));
221 if (seat_con == NULL) { 221 if (seat_con == NULL) {
222 wlr_log(L_ERROR, "could not allocate seat container"); 222 wlr_log(WLR_ERROR, "could not allocate seat container");
223 return NULL; 223 return NULL;
224 } 224 }
225 225
@@ -301,7 +301,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
301 301
302 struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); 302 struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon));
303 if (icon == NULL) { 303 if (icon == NULL) {
304 wlr_log(L_ERROR, "Allocation failed"); 304 wlr_log(WLR_ERROR, "Allocation failed");
305 return; 305 return;
306 } 306 }
307 icon->seat = seat; 307 icon->seat = seat;
@@ -391,7 +391,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
391 struct input_config *ic = input_device_get_config( 391 struct input_config *ic = input_device_get_config(
392 sway_device->input_device); 392 sway_device->input_device);
393 if (ic != NULL) { 393 if (ic != NULL) {
394 wlr_log(L_DEBUG, "Applying input config to %s", 394 wlr_log(WLR_DEBUG, "Applying input config to %s",
395 sway_device->input_device->identifier); 395 sway_device->input_device->identifier);
396 396
397 mapped_to_output = ic->mapped_to_output; 397 mapped_to_output = ic->mapped_to_output;
@@ -401,7 +401,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
401 mapped_to_output = sway_device->input_device->wlr_device->output_name; 401 mapped_to_output = sway_device->input_device->wlr_device->output_name;
402 } 402 }
403 if (mapped_to_output != NULL) { 403 if (mapped_to_output != NULL) {
404 wlr_log(L_DEBUG, "Mapping input device %s to output %s", 404 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
405 sway_device->input_device->identifier, mapped_to_output); 405 sway_device->input_device->identifier, mapped_to_output);
406 struct sway_container *output = NULL; 406 struct sway_container *output = NULL;
407 for (int i = 0; i < root_container.children->length; ++i) { 407 for (int i = 0; i < root_container.children->length; ++i) {
@@ -415,7 +415,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
415 wlr_cursor_map_input_to_output(seat->cursor->cursor, 415 wlr_cursor_map_input_to_output(seat->cursor->cursor,
416 sway_device->input_device->wlr_device, 416 sway_device->input_device->wlr_device,
417 output->sway_output->wlr_output); 417 output->sway_output->wlr_output);
418 wlr_log(L_DEBUG, "Mapped to output %s", output->name); 418 wlr_log(WLR_DEBUG, "Mapped to output %s", output->name);
419 } 419 }
420 } 420 }
421} 421}
@@ -495,7 +495,7 @@ void seat_configure_device(struct sway_seat *seat,
495 seat_configure_tablet_tool(seat, seat_device); 495 seat_configure_tablet_tool(seat, seat_device);
496 break; 496 break;
497 case WLR_INPUT_DEVICE_TABLET_PAD: 497 case WLR_INPUT_DEVICE_TABLET_PAD:
498 wlr_log(L_DEBUG, "TODO: configure tablet pad"); 498 wlr_log(WLR_DEBUG, "TODO: configure tablet pad");
499 break; 499 break;
500 } 500 }
501} 501}
@@ -510,11 +510,11 @@ void seat_add_device(struct sway_seat *seat,
510 struct sway_seat_device *seat_device = 510 struct sway_seat_device *seat_device =
511 calloc(1, sizeof(struct sway_seat_device)); 511 calloc(1, sizeof(struct sway_seat_device));
512 if (!seat_device) { 512 if (!seat_device) {
513 wlr_log(L_DEBUG, "could not allocate seat device"); 513 wlr_log(WLR_DEBUG, "could not allocate seat device");
514 return; 514 return;
515 } 515 }
516 516
517 wlr_log(L_DEBUG, "adding device %s to seat %s", 517 wlr_log(WLR_DEBUG, "adding device %s to seat %s",
518 input_device->identifier, seat->wlr_seat->name); 518 input_device->identifier, seat->wlr_seat->name);
519 519
520 seat_device->sway_seat = seat; 520 seat_device->sway_seat = seat;
@@ -533,7 +533,7 @@ void seat_remove_device(struct sway_seat *seat,
533 return; 533 return;
534 } 534 }
535 535
536 wlr_log(L_DEBUG, "removing device %s from seat %s", 536 wlr_log(WLR_DEBUG, "removing device %s from seat %s",
537 input_device->identifier, seat->wlr_seat->name); 537 input_device->identifier, seat->wlr_seat->name);
538 538
539 seat_device_destroy(seat_device); 539 seat_device_destroy(seat_device);
@@ -594,6 +594,12 @@ static void seat_send_unfocus(struct sway_container *container,
594 } 594 }
595} 595}
596 596
597static int handle_urgent_timeout(void *data) {
598 struct sway_view *view = data;
599 view_set_urgent(view, false);
600 return 0;
601}
602
597void seat_set_focus_warp(struct sway_seat *seat, 603void seat_set_focus_warp(struct sway_seat *seat,
598 struct sway_container *container, bool warp) { 604 struct sway_container *container, bool warp) {
599 if (seat->focused_layer) { 605 if (seat->focused_layer) {
@@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
649 while (parent) { 655 while (parent) {
650 wl_list_remove(&parent->link); 656 wl_list_remove(&parent->link);
651 wl_list_insert(&seat->focus_stack, &parent->link); 657 wl_list_insert(&seat->focus_stack, &parent->link);
658 container_set_dirty(parent->container);
652 659
653 parent = 660 parent =
654 seat_container_from_container(seat, 661 seat_container_from_container(seat,
@@ -661,9 +668,33 @@ void seat_set_focus_warp(struct sway_seat *seat,
661 if (last_focus) { 668 if (last_focus) {
662 seat_send_unfocus(last_focus, seat); 669 seat_send_unfocus(last_focus, seat);
663 } 670 }
664
665 seat_send_focus(container, seat); 671 seat_send_focus(container, seat);
666 container_damage_whole(container->parent); 672
673 container_set_dirty(container);
674 container_set_dirty(container->parent); // for focused_inactive_child
675 if (last_focus) {
676 container_set_dirty(last_focus);
677 }
678 }
679
680 // If urgent, start a timer to unset it
681 if (container && container->type == C_VIEW &&
682 view_is_urgent(container->sway_view) &&
683 !container->sway_view->urgent_timer) {
684 struct sway_view *view = container->sway_view;
685 view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,
686 handle_urgent_timeout, view);
687 wl_event_source_timer_update(view->urgent_timer, 1000);
688 }
689
690 // If we've focused a floating container, bring it to the front.
691 // We do this by putting it at the end of the floating list.
692 // This must happen for both the pending and current children lists.
693 if (container && container_is_floating(container)) {
694 list_move_to_end(container->parent->children, container);
695 if (container_has_ancestor(container, container->current.parent)) {
696 list_move_to_end(container->parent->current.children, container);
697 }
667 } 698 }
668 699
669 // clean up unfocused empty workspace on new output 700 // clean up unfocused empty workspace on new output
@@ -707,10 +738,6 @@ void seat_set_focus_warp(struct sway_seat *seat,
707 } 738 }
708 } 739 }
709 740
710 if (last_focus) {
711 container_damage_whole(last_focus);
712 }
713
714 if (last_workspace && last_workspace != new_workspace) { 741 if (last_workspace && last_workspace != new_workspace) {
715 cursor_send_pointer_motion(seat->cursor, 0, true); 742 cursor_send_pointer_motion(seat->cursor, 0, true);
716 } 743 }
@@ -752,7 +779,7 @@ void seat_set_focus_layer(struct sway_seat *seat,
752 struct sway_container *previous = 779 struct sway_container *previous =
753 seat_get_focus_inactive(seat, &root_container); 780 seat_get_focus_inactive(seat, &root_container);
754 if (previous) { 781 if (previous) {
755 wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, 782 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
756 container_type_to_str(previous->type), previous->name); 783 container_type_to_str(previous->type), previous->name);
757 // Hack to get seat to re-focus the return value of get_focus 784 // Hack to get seat to re-focus the return value of get_focus
758 seat_set_focus(seat, previous->parent); 785 seat_set_focus(seat, previous->parent);
@@ -830,18 +857,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
830 return NULL; 857 return NULL;
831} 858}
832 859
833struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
834 struct sway_container *container) {
835 struct sway_seat_container *current = NULL;
836 wl_list_for_each(current, &seat->focus_stack, link) {
837 if (current->container->current.parent == container &&
838 current->container->current.layout != L_FLOATING) {
839 return current->container;
840 }
841 }
842 return NULL;
843}
844
845struct sway_container *seat_get_focus(struct sway_seat *seat) { 860struct sway_container *seat_get_focus(struct sway_seat *seat) {
846 if (!seat->has_focus) { 861 if (!seat->has_focus) {
847 return NULL; 862 return NULL;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index b9289e25..c49ea47e 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -2,6 +2,7 @@
2#include <stdio.h> 2#include <stdio.h>
3#include <ctype.h> 3#include <ctype.h>
4#include "log.h" 4#include "log.h"
5#include "sway/config.h"
5#include "sway/ipc-json.h" 6#include "sway/ipc-json.h"
6#include "sway/tree/container.h" 7#include "sway/tree/container.h"
7#include "sway/tree/workspace.h" 8#include "sway/tree/workspace.h"
@@ -41,6 +42,7 @@ json_object *ipc_json_get_version() {
41 json_object_object_add(version, "major", json_object_new_int(major)); 42 json_object_object_add(version, "major", json_object_new_int(major));
42 json_object_object_add(version, "minor", json_object_new_int(minor)); 43 json_object_object_add(version, "minor", json_object_new_int(minor));
43 json_object_object_add(version, "patch", json_object_new_int(patch)); 44 json_object_object_add(version, "patch", json_object_new_int(patch));
45 json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path));
44 46
45 return version; 47 return version;
46} 48}
@@ -168,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace,
168 json_object_object_add(object, "output", workspace->parent ? 170 json_object_object_add(object, "output", workspace->parent ?
169 json_object_new_string(workspace->parent->name) : NULL); 171 json_object_new_string(workspace->parent->name) : NULL);
170 json_object_object_add(object, "type", json_object_new_string("workspace")); 172 json_object_object_add(object, "type", json_object_new_string("workspace"));
171 json_object_object_add(object, "urgent", json_object_new_boolean(false)); 173 json_object_object_add(object, "urgent",
174 json_object_new_boolean(workspace->sway_workspace->urgent));
172 json_object_object_add(object, "representation", workspace->formatted_title ? 175 json_object_object_add(object, "representation", workspace->formatted_title ?
173 json_object_new_string(workspace->formatted_title) : NULL); 176 json_object_new_string(workspace->formatted_title) : NULL);
174 177
@@ -194,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
194 json_object_object_add(object, "layout", 197 json_object_object_add(object, "layout",
195 json_object_new_string(ipc_json_layout_description(c->layout))); 198 json_object_new_string(ipc_json_layout_description(c->layout)));
196 } 199 }
200
201 bool urgent = c->type == C_VIEW ?
202 view_is_urgent(c->sway_view) : container_has_urgent_child(c);
203 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
197} 204}
198 205
199static void focus_inactive_children_iterator(struct sway_container *c, void *data) { 206static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index abdaa237..be703915 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -17,6 +17,8 @@
17#include <unistd.h> 17#include <unistd.h>
18#include <wayland-server.h> 18#include <wayland-server.h>
19#include "sway/commands.h" 19#include "sway/commands.h"
20#include "sway/config.h"
21#include "sway/desktop/transaction.h"
20#include "sway/ipc-json.h" 22#include "sway/ipc-json.h"
21#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
22#include "sway/output.h" 24#include "sway/output.h"
@@ -138,32 +140,32 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
138int ipc_handle_connection(int fd, uint32_t mask, void *data) { 140int ipc_handle_connection(int fd, uint32_t mask, void *data) {
139 (void) fd; 141 (void) fd;
140 struct sway_server *server = data; 142 struct sway_server *server = data;
141 wlr_log(L_DEBUG, "Event on IPC listening socket"); 143 wlr_log(WLR_DEBUG, "Event on IPC listening socket");
142 assert(mask == WL_EVENT_READABLE); 144 assert(mask == WL_EVENT_READABLE);
143 145
144 int client_fd = accept(ipc_socket, NULL, NULL); 146 int client_fd = accept(ipc_socket, NULL, NULL);
145 if (client_fd == -1) { 147 if (client_fd == -1) {
146 wlr_log_errno(L_ERROR, "Unable to accept IPC client connection"); 148 wlr_log_errno(WLR_ERROR, "Unable to accept IPC client connection");
147 return 0; 149 return 0;
148 } 150 }
149 151
150 int flags; 152 int flags;
151 if ((flags = fcntl(client_fd, F_GETFD)) == -1 153 if ((flags = fcntl(client_fd, F_GETFD)) == -1
152 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { 154 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
153 wlr_log_errno(L_ERROR, "Unable to set CLOEXEC on IPC client socket"); 155 wlr_log_errno(WLR_ERROR, "Unable to set CLOEXEC on IPC client socket");
154 close(client_fd); 156 close(client_fd);
155 return 0; 157 return 0;
156 } 158 }
157 if ((flags = fcntl(client_fd, F_GETFL)) == -1 159 if ((flags = fcntl(client_fd, F_GETFL)) == -1
158 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) { 160 || fcntl(client_fd, F_SETFL, flags|O_NONBLOCK) == -1) {
159 wlr_log_errno(L_ERROR, "Unable to set NONBLOCK on IPC client socket"); 161 wlr_log_errno(WLR_ERROR, "Unable to set NONBLOCK on IPC client socket");
160 close(client_fd); 162 close(client_fd);
161 return 0; 163 return 0;
162 } 164 }
163 165
164 struct ipc_client *client = malloc(sizeof(struct ipc_client)); 166 struct ipc_client *client = malloc(sizeof(struct ipc_client));
165 if (!client) { 167 if (!client) {
166 wlr_log(L_ERROR, "Unable to allocate ipc client"); 168 wlr_log(WLR_ERROR, "Unable to allocate ipc client");
167 close(client_fd); 169 close(client_fd);
168 return 0; 170 return 0;
169 } 171 }
@@ -179,12 +181,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
179 client->write_buffer_len = 0; 181 client->write_buffer_len = 0;
180 client->write_buffer = malloc(client->write_buffer_size); 182 client->write_buffer = malloc(client->write_buffer_size);
181 if (!client->write_buffer) { 183 if (!client->write_buffer) {
182 wlr_log(L_ERROR, "Unable to allocate ipc client write buffer"); 184 wlr_log(WLR_ERROR, "Unable to allocate ipc client write buffer");
183 close(client_fd); 185 close(client_fd);
184 return 0; 186 return 0;
185 } 187 }
186 188
187 wlr_log(L_DEBUG, "New client: fd %d", client_fd); 189 wlr_log(WLR_DEBUG, "New client: fd %d", client_fd);
188 list_add(ipc_client_list, client); 190 list_add(ipc_client_list, client);
189 return 0; 191 return 0;
190} 192}
@@ -195,22 +197,22 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
195 struct ipc_client *client = data; 197 struct ipc_client *client = data;
196 198
197 if (mask & WL_EVENT_ERROR) { 199 if (mask & WL_EVENT_ERROR) {
198 wlr_log(L_ERROR, "IPC Client socket error, removing client"); 200 wlr_log(WLR_ERROR, "IPC Client socket error, removing client");
199 ipc_client_disconnect(client); 201 ipc_client_disconnect(client);
200 return 0; 202 return 0;
201 } 203 }
202 204
203 if (mask & WL_EVENT_HANGUP) { 205 if (mask & WL_EVENT_HANGUP) {
204 wlr_log(L_DEBUG, "Client %d hung up", client->fd); 206 wlr_log(WLR_DEBUG, "Client %d hung up", client->fd);
205 ipc_client_disconnect(client); 207 ipc_client_disconnect(client);
206 return 0; 208 return 0;
207 } 209 }
208 210
209 wlr_log(L_DEBUG, "Client %d readable", client->fd); 211 wlr_log(WLR_DEBUG, "Client %d readable", client->fd);
210 212
211 int read_available; 213 int read_available;
212 if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 214 if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
213 wlr_log_errno(L_INFO, "Unable to read IPC socket buffer size"); 215 wlr_log_errno(WLR_INFO, "Unable to read IPC socket buffer size");
214 ipc_client_disconnect(client); 216 ipc_client_disconnect(client);
215 return 0; 217 return 0;
216 } 218 }
@@ -232,13 +234,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
232 // Should be fully available, because read_available >= ipc_header_size 234 // Should be fully available, because read_available >= ipc_header_size
233 ssize_t received = recv(client_fd, buf, ipc_header_size, 0); 235 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
234 if (received == -1) { 236 if (received == -1) {
235 wlr_log_errno(L_INFO, "Unable to receive header from IPC client"); 237 wlr_log_errno(WLR_INFO, "Unable to receive header from IPC client");
236 ipc_client_disconnect(client); 238 ipc_client_disconnect(client);
237 return 0; 239 return 0;
238 } 240 }
239 241
240 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { 242 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
241 wlr_log(L_DEBUG, "IPC header check failed"); 243 wlr_log(WLR_DEBUG, "IPC header check failed");
242 ipc_client_disconnect(client); 244 ipc_client_disconnect(client);
243 return 0; 245 return 0;
244 } 246 }
@@ -272,7 +274,7 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)
272 } 274 }
273 client->current_command = event; 275 client->current_command = event;
274 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { 276 if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) {
275 wlr_log_errno(L_INFO, "Unable to send reply to IPC client"); 277 wlr_log_errno(WLR_INFO, "Unable to send reply to IPC client");
276 /* ipc_send_reply destroys client on error, which also 278 /* ipc_send_reply destroys client on error, which also
277 * removes it from the list, so we need to process 279 * removes it from the list, so we need to process
278 * current index again */ 280 * current index again */
@@ -286,7 +288,7 @@ void ipc_event_workspace(struct sway_container *old,
286 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { 288 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
287 return; 289 return;
288 } 290 }
289 wlr_log(L_DEBUG, "Sending workspace::%s event", change); 291 wlr_log(WLR_DEBUG, "Sending workspace::%s event", change);
290 json_object *obj = json_object_new_object(); 292 json_object *obj = json_object_new_object();
291 json_object_object_add(obj, "change", json_object_new_string(change)); 293 json_object_object_add(obj, "change", json_object_new_string(change));
292 if (strcmp("focus", change) == 0) { 294 if (strcmp("focus", change) == 0) {
@@ -314,7 +316,7 @@ void ipc_event_window(struct sway_container *window, const char *change) {
314 if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) { 316 if (!ipc_has_event_listeners(IPC_EVENT_WINDOW)) {
315 return; 317 return;
316 } 318 }
317 wlr_log(L_DEBUG, "Sending window::%s event", change); 319 wlr_log(WLR_DEBUG, "Sending window::%s event", change);
318 json_object *obj = json_object_new_object(); 320 json_object *obj = json_object_new_object();
319 json_object_object_add(obj, "change", json_object_new_string(change)); 321 json_object_object_add(obj, "change", json_object_new_string(change));
320 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); 322 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window));
@@ -328,7 +330,7 @@ void ipc_event_barconfig_update(struct bar_config *bar) {
328 if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) { 330 if (!ipc_has_event_listeners(IPC_EVENT_BARCONFIG_UPDATE)) {
329 return; 331 return;
330 } 332 }
331 wlr_log(L_DEBUG, "Sending barconfig_update event"); 333 wlr_log(WLR_DEBUG, "Sending barconfig_update event");
332 json_object *json = ipc_json_describe_bar_config(bar); 334 json_object *json = ipc_json_describe_bar_config(bar);
333 335
334 const char *json_string = json_object_to_json_string(json); 336 const char *json_string = json_object_to_json_string(json);
@@ -340,7 +342,7 @@ void ipc_event_mode(const char *mode, bool pango) {
340 if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { 342 if (!ipc_has_event_listeners(IPC_EVENT_MODE)) {
341 return; 343 return;
342 } 344 }
343 wlr_log(L_DEBUG, "Sending mode::%s event", mode); 345 wlr_log(WLR_DEBUG, "Sending mode::%s event", mode);
344 json_object *obj = json_object_new_object(); 346 json_object *obj = json_object_new_object();
345 json_object_object_add(obj, "change", json_object_new_string(mode)); 347 json_object_object_add(obj, "change", json_object_new_string(mode));
346 json_object_object_add(obj, "pango_markup", 348 json_object_object_add(obj, "pango_markup",
@@ -355,13 +357,13 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
355 struct ipc_client *client = data; 357 struct ipc_client *client = data;
356 358
357 if (mask & WL_EVENT_ERROR) { 359 if (mask & WL_EVENT_ERROR) {
358 wlr_log(L_ERROR, "IPC Client socket error, removing client"); 360 wlr_log(WLR_ERROR, "IPC Client socket error, removing client");
359 ipc_client_disconnect(client); 361 ipc_client_disconnect(client);
360 return 0; 362 return 0;
361 } 363 }
362 364
363 if (mask & WL_EVENT_HANGUP) { 365 if (mask & WL_EVENT_HANGUP) {
364 wlr_log(L_DEBUG, "Client %d hung up", client->fd); 366 wlr_log(WLR_DEBUG, "Client %d hung up", client->fd);
365 ipc_client_disconnect(client); 367 ipc_client_disconnect(client);
366 return 0; 368 return 0;
367 } 369 }
@@ -370,14 +372,14 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
370 return 0; 372 return 0;
371 } 373 }
372 374
373 wlr_log(L_DEBUG, "Client %d writable", client->fd); 375 wlr_log(WLR_DEBUG, "Client %d writable", client->fd);
374 376
375 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); 377 ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
376 378
377 if (written == -1 && errno == EAGAIN) { 379 if (written == -1 && errno == EAGAIN) {
378 return 0; 380 return 0;
379 } else if (written == -1) { 381 } else if (written == -1) {
380 wlr_log_errno(L_INFO, "Unable to send data from queue to IPC client"); 382 wlr_log_errno(WLR_INFO, "Unable to send data from queue to IPC client");
381 ipc_client_disconnect(client); 383 ipc_client_disconnect(client);
382 return 0; 384 return 0;
383 } 385 }
@@ -400,7 +402,7 @@ void ipc_client_disconnect(struct ipc_client *client) {
400 402
401 shutdown(client->fd, SHUT_RDWR); 403 shutdown(client->fd, SHUT_RDWR);
402 404
403 wlr_log(L_INFO, "IPC Client %d disconnected", client->fd); 405 wlr_log(WLR_INFO, "IPC Client %d disconnected", client->fd);
404 wl_event_source_remove(client->event_source); 406 wl_event_source_remove(client->event_source);
405 if (client->writable_event_source) { 407 if (client->writable_event_source) {
406 wl_event_source_remove(client->writable_event_source); 408 wl_event_source_remove(client->writable_event_source);
@@ -461,7 +463,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
461 463
462 char *buf = malloc(client->payload_length + 1); 464 char *buf = malloc(client->payload_length + 1);
463 if (!buf) { 465 if (!buf) {
464 wlr_log_errno(L_INFO, "Unable to allocate IPC payload"); 466 wlr_log_errno(WLR_INFO, "Unable to allocate IPC payload");
465 ipc_client_disconnect(client); 467 ipc_client_disconnect(client);
466 return; 468 return;
467 } 469 }
@@ -470,7 +472,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
470 ssize_t received = recv(client->fd, buf, client->payload_length, 0); 472 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
471 if (received == -1) 473 if (received == -1)
472 { 474 {
473 wlr_log_errno(L_INFO, "Unable to receive payload from IPC client"); 475 wlr_log_errno(WLR_INFO, "Unable to receive payload from IPC client");
474 ipc_client_disconnect(client); 476 ipc_client_disconnect(client);
475 free(buf); 477 free(buf);
476 return; 478 return;
@@ -483,6 +485,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
483 case IPC_COMMAND: 485 case IPC_COMMAND:
484 { 486 {
485 struct cmd_results *results = execute_command(buf, NULL); 487 struct cmd_results *results = execute_command(buf, NULL);
488 transaction_commit_dirty();
486 char *json = cmd_results_to_json(results); 489 char *json = cmd_results_to_json(results);
487 int length = strlen(json); 490 int length = strlen(json);
488 client_valid = ipc_send_reply(client, json, (uint32_t)length); 491 client_valid = ipc_send_reply(client, json, (uint32_t)length);
@@ -533,7 +536,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
533 struct json_object *request = json_tokener_parse(buf); 536 struct json_object *request = json_tokener_parse(buf);
534 if (request == NULL) { 537 if (request == NULL) {
535 client_valid = ipc_send_reply(client, "{\"success\": false}", 18); 538 client_valid = ipc_send_reply(client, "{\"success\": false}", 18);
536 wlr_log_errno(L_INFO, "Failed to read request"); 539 wlr_log_errno(WLR_INFO, "Failed to read request");
537 goto exit_cleanup; 540 goto exit_cleanup;
538 } 541 }
539 542
@@ -556,7 +559,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
556 client_valid = 559 client_valid =
557 ipc_send_reply(client, "{\"success\": false}", 18); 560 ipc_send_reply(client, "{\"success\": false}", 18);
558 json_object_put(request); 561 json_object_put(request);
559 wlr_log_errno(L_INFO, "Failed to parse request"); 562 wlr_log_errno(WLR_INFO, "Failed to parse request");
560 goto exit_cleanup; 563 goto exit_cleanup;
561 } 564 }
562 } 565 }
@@ -667,8 +670,33 @@ void ipc_client_handle_command(struct ipc_client *client) {
667 goto exit_cleanup; 670 goto exit_cleanup;
668 } 671 }
669 672
673 case IPC_GET_BINDING_MODES:
674 {
675 json_object *modes = json_object_new_array();
676 for (int i = 0; i < config->modes->length; i++) {
677 struct sway_mode *mode = config->modes->items[i];
678 json_object_array_add(modes, json_object_new_string(mode->name));
679 }
680 const char *json_string = json_object_to_json_string(modes);
681 client_valid =
682 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
683 json_object_put(modes); // free
684 goto exit_cleanup;
685 }
686
687 case IPC_GET_CONFIG:
688 {
689 json_object *json = json_object_new_object();
690 json_object_object_add(json, "config", json_object_new_string(config->current_config));
691 const char *json_string = json_object_to_json_string(json);
692 client_valid =
693 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
694 json_object_put(json); // free
695 goto exit_cleanup;
696 }
697
670 default: 698 default:
671 wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); 699 wlr_log(WLR_INFO, "Unknown IPC command type %i", client->current_command);
672 goto exit_cleanup; 700 goto exit_cleanup;
673 } 701 }
674 702
@@ -696,14 +724,14 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
696 } 724 }
697 725
698 if (client->write_buffer_size > 4e6) { // 4 MB 726 if (client->write_buffer_size > 4e6) { // 4 MB
699 wlr_log(L_ERROR, "Client write buffer too big, disconnecting client"); 727 wlr_log(WLR_ERROR, "Client write buffer too big, disconnecting client");
700 ipc_client_disconnect(client); 728 ipc_client_disconnect(client);
701 return false; 729 return false;
702 } 730 }
703 731
704 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size); 732 char *new_buffer = realloc(client->write_buffer, client->write_buffer_size);
705 if (!new_buffer) { 733 if (!new_buffer) {
706 wlr_log(L_ERROR, "Unable to reallocate ipc client write buffer"); 734 wlr_log(WLR_ERROR, "Unable to reallocate ipc client write buffer");
707 ipc_client_disconnect(client); 735 ipc_client_disconnect(client);
708 return false; 736 return false;
709 } 737 }
@@ -720,6 +748,6 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay
720 ipc_client_handle_writable, client); 748 ipc_client_handle_writable, client);
721 } 749 }
722 750
723 wlr_log(L_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); 751 wlr_log(WLR_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload);
724 return true; 752 return true;
725} 753}
diff --git a/sway/main.c b/sway/main.c
index ec7353be..1a55b519 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -20,6 +20,7 @@
20#include "sway/commands.h" 20#include "sway/commands.h"
21#include "sway/config.h" 21#include "sway/config.h"
22#include "sway/debug.h" 22#include "sway/debug.h"
23#include "sway/desktop/transaction.h"
23#include "sway/server.h" 24#include "sway/server.h"
24#include "sway/tree/layout.h" 25#include "sway/tree/layout.h"
25#include "sway/ipc-server.h" 26#include "sway/ipc-server.h"
@@ -129,7 +130,7 @@ static void log_env() {
129 "SWAYSOCK" 130 "SWAYSOCK"
130 }; 131 };
131 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) { 132 for (size_t i = 0; i < sizeof(log_vars) / sizeof(char *); ++i) {
132 wlr_log(L_INFO, "%s=%s", log_vars[i], getenv(log_vars[i])); 133 wlr_log(WLR_INFO, "%s=%s", log_vars[i], getenv(log_vars[i]));
133 } 134 }
134} 135}
135 136
@@ -144,14 +145,14 @@ static void log_distro() {
144 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) { 145 for (size_t i = 0; i < sizeof(paths) / sizeof(char *); ++i) {
145 FILE *f = fopen(paths[i], "r"); 146 FILE *f = fopen(paths[i], "r");
146 if (f) { 147 if (f) {
147 wlr_log(L_INFO, "Contents of %s:", paths[i]); 148 wlr_log(WLR_INFO, "Contents of %s:", paths[i]);
148 while (!feof(f)) { 149 while (!feof(f)) {
149 char *line; 150 char *line;
150 if (!(line = read_line(f))) { 151 if (!(line = read_line(f))) {
151 break; 152 break;
152 } 153 }
153 if (*line) { 154 if (*line) {
154 wlr_log(L_INFO, "%s", line); 155 wlr_log(WLR_INFO, "%s", line);
155 } 156 }
156 free(line); 157 free(line);
157 } 158 }
@@ -163,7 +164,7 @@ static void log_distro() {
163static void log_kernel() { 164static void log_kernel() {
164 FILE *f = popen("uname -a", "r"); 165 FILE *f = popen("uname -a", "r");
165 if (!f) { 166 if (!f) {
166 wlr_log(L_INFO, "Unable to determine kernel version"); 167 wlr_log(WLR_INFO, "Unable to determine kernel version");
167 return; 168 return;
168 } 169 }
169 while (!feof(f)) { 170 while (!feof(f)) {
@@ -172,7 +173,7 @@ static void log_kernel() {
172 break; 173 break;
173 } 174 }
174 if (*line) { 175 if (*line) {
175 wlr_log(L_INFO, "%s", line); 176 wlr_log(WLR_INFO, "%s", line);
176 } 177 }
177 free(line); 178 free(line);
178 } 179 }
@@ -183,14 +184,14 @@ static void security_sanity_check() {
183 // TODO: Notify users visually if this has issues 184 // TODO: Notify users visually if this has issues
184 struct stat s; 185 struct stat s;
185 if (stat("/proc", &s)) { 186 if (stat("/proc", &s)) {
186 wlr_log(L_ERROR, 187 wlr_log(WLR_ERROR,
187 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!"); 188 "!! DANGER !! /proc is not available - sway CANNOT enforce security rules!");
188 } 189 }
189#ifdef __linux__ 190#ifdef __linux__
190 cap_flag_value_t v; 191 cap_flag_value_t v;
191 cap_t cap = cap_get_proc(); 192 cap_t cap = cap_get_proc();
192 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) { 193 if (!cap || cap_get_flag(cap, CAP_SYS_PTRACE, CAP_PERMITTED, &v) != 0 || v != CAP_SET) {
193 wlr_log(L_ERROR, 194 wlr_log(WLR_ERROR,
194 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users."); 195 "!! DANGER !! Sway does not have CAP_SYS_PTRACE and cannot enforce security rules for processes running as other users.");
195 } 196 }
196 if (cap) { 197 if (cap) {
@@ -206,13 +207,13 @@ static void executable_sanity_check() {
206 stat(exe, &sb); 207 stat(exe, &sb);
207 // We assume that cap_get_file returning NULL implies ENODATA 208 // We assume that cap_get_file returning NULL implies ENODATA
208 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { 209 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
209 wlr_log(L_ERROR, 210 wlr_log(WLR_ERROR,
210 "sway executable has both the s(g)uid bit AND file caps set."); 211 "sway executable has both the s(g)uid bit AND file caps set.");
211 wlr_log(L_ERROR, 212 wlr_log(WLR_ERROR,
212 "This is strongly discouraged (and completely broken)."); 213 "This is strongly discouraged (and completely broken).");
213 wlr_log(L_ERROR, 214 wlr_log(WLR_ERROR,
214 "Please clear one of them (either the suid bit, or the file caps)."); 215 "Please clear one of them (either the suid bit, or the file caps).");
215 wlr_log(L_ERROR, 216 wlr_log(WLR_ERROR,
216 "If unsure, strip the file caps."); 217 "If unsure, strip the file caps.");
217 exit(EXIT_FAILURE); 218 exit(EXIT_FAILURE);
218 } 219 }
@@ -223,16 +224,16 @@ static void executable_sanity_check() {
223static void drop_permissions(bool keep_caps) { 224static void drop_permissions(bool keep_caps) {
224 if (getuid() != geteuid() || getgid() != getegid()) { 225 if (getuid() != geteuid() || getgid() != getegid()) {
225 if (setgid(getgid()) != 0) { 226 if (setgid(getgid()) != 0) {
226 wlr_log(L_ERROR, "Unable to drop root"); 227 wlr_log(WLR_ERROR, "Unable to drop root");
227 exit(EXIT_FAILURE); 228 exit(EXIT_FAILURE);
228 } 229 }
229 if (setuid(getuid()) != 0) { 230 if (setuid(getuid()) != 0) {
230 wlr_log(L_ERROR, "Unable to drop root"); 231 wlr_log(WLR_ERROR, "Unable to drop root");
231 exit(EXIT_FAILURE); 232 exit(EXIT_FAILURE);
232 } 233 }
233 } 234 }
234 if (setuid(0) != -1) { 235 if (setuid(0) != -1) {
235 wlr_log(L_ERROR, "Root privileges can be restored."); 236 wlr_log(WLR_ERROR, "Root privileges can be restored.");
236 exit(EXIT_FAILURE); 237 exit(EXIT_FAILURE);
237 } 238 }
238#ifdef __linux__ 239#ifdef __linux__
@@ -240,17 +241,29 @@ static void drop_permissions(bool keep_caps) {
240 // Drop every cap except CAP_SYS_PTRACE 241 // Drop every cap except CAP_SYS_PTRACE
241 cap_t caps = cap_init(); 242 cap_t caps = cap_init();
242 cap_value_t keep = CAP_SYS_PTRACE; 243 cap_value_t keep = CAP_SYS_PTRACE;
243 wlr_log(L_INFO, "Dropping extra capabilities"); 244 wlr_log(WLR_INFO, "Dropping extra capabilities");
244 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || 245 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
245 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || 246 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
246 cap_set_proc(caps)) { 247 cap_set_proc(caps)) {
247 wlr_log(L_ERROR, "Failed to drop extra capabilities"); 248 wlr_log(WLR_ERROR, "Failed to drop extra capabilities");
248 exit(EXIT_FAILURE); 249 exit(EXIT_FAILURE);
249 } 250 }
250 } 251 }
251#endif 252#endif
252} 253}
253 254
255void enable_debug_flag(const char *flag) {
256 if (strcmp(flag, "render-tree") == 0) {
257 enable_debug_tree = true;
258 } else if (strncmp(flag, "damage=", 7) == 0) {
259 damage_debug = &flag[7];
260 } else if (strcmp(flag, "txn-debug") == 0) {
261 txn_debug = true;
262 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
263 txn_timeout_ms = atoi(&flag[12]);
264 }
265}
266
254int main(int argc, char **argv) { 267int main(int argc, char **argv) {
255 static int verbose = 0, debug = 0, validate = 0; 268 static int verbose = 0, debug = 0, validate = 0;
256 269
@@ -290,7 +303,7 @@ int main(int argc, char **argv) {
290 int c; 303 int c;
291 while (1) { 304 while (1) {
292 int option_index = 0; 305 int option_index = 0;
293 c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); 306 c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index);
294 if (c == -1) { 307 if (c == -1) {
295 break; 308 break;
296 } 309 }
@@ -309,7 +322,7 @@ int main(int argc, char **argv) {
309 debug = 1; 322 debug = 1;
310 break; 323 break;
311 case 'D': // extended debug options 324 case 'D': // extended debug options
312 enable_debug_tree = true; 325 enable_debug_flag(optarg);
313 break; 326 break;
314 case 'v': // version 327 case 'v': // version
315 fprintf(stdout, "sway version " SWAY_VERSION "\n"); 328 fprintf(stdout, "sway version " SWAY_VERSION "\n");
@@ -335,22 +348,22 @@ int main(int argc, char **argv) {
335 348
336 // TODO: switch logging over to wlroots? 349 // TODO: switch logging over to wlroots?
337 if (debug) { 350 if (debug) {
338 wlr_log_init(L_DEBUG, NULL); 351 wlr_log_init(WLR_DEBUG, NULL);
339 } else if (verbose || validate) { 352 } else if (verbose || validate) {
340 wlr_log_init(L_INFO, NULL); 353 wlr_log_init(WLR_INFO, NULL);
341 } else { 354 } else {
342 wlr_log_init(L_ERROR, NULL); 355 wlr_log_init(WLR_ERROR, NULL);
343 } 356 }
344 357
345 if (optind < argc) { // Behave as IPC client 358 if (optind < argc) { // Behave as IPC client
346 if(optind != 1) { 359 if(optind != 1) {
347 wlr_log(L_ERROR, "Don't use options with the IPC client"); 360 wlr_log(WLR_ERROR, "Don't use options with the IPC client");
348 exit(EXIT_FAILURE); 361 exit(EXIT_FAILURE);
349 } 362 }
350 drop_permissions(false); 363 drop_permissions(false);
351 char *socket_path = getenv("SWAYSOCK"); 364 char *socket_path = getenv("SWAYSOCK");
352 if (!socket_path) { 365 if (!socket_path) {
353 wlr_log(L_ERROR, "Unable to retrieve socket path"); 366 wlr_log(WLR_ERROR, "Unable to retrieve socket path");
354 exit(EXIT_FAILURE); 367 exit(EXIT_FAILURE);
355 } 368 }
356 char *command = join_args(argv + optind, argc - optind); 369 char *command = join_args(argv + optind, argc - optind);
@@ -369,7 +382,7 @@ int main(int argc, char **argv) {
369 if (getuid() != geteuid() || getgid() != getegid()) { 382 if (getuid() != geteuid() || getgid() != getegid()) {
370 // Retain capabilities after setuid() 383 // Retain capabilities after setuid()
371 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { 384 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
372 wlr_log(L_ERROR, "Cannot keep caps after setuid()"); 385 wlr_log(WLR_ERROR, "Cannot keep caps after setuid()");
373 exit(EXIT_FAILURE); 386 exit(EXIT_FAILURE);
374 } 387 }
375 suid = true; 388 suid = true;
@@ -390,7 +403,7 @@ int main(int argc, char **argv) {
390 // prevent ipc from crashing sway 403 // prevent ipc from crashing sway
391 signal(SIGPIPE, SIG_IGN); 404 signal(SIGPIPE, SIG_IGN);
392 405
393 wlr_log(L_INFO, "Starting sway version " SWAY_VERSION); 406 wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION);
394 407
395 layout_init(); 408 layout_init();
396 409
@@ -423,18 +436,19 @@ int main(int argc, char **argv) {
423 char *line = config->cmd_queue->items[0]; 436 char *line = config->cmd_queue->items[0];
424 struct cmd_results *res = execute_command(line, NULL); 437 struct cmd_results *res = execute_command(line, NULL);
425 if (res->status != CMD_SUCCESS) { 438 if (res->status != CMD_SUCCESS) {
426 wlr_log(L_ERROR, "Error on line '%s': %s", line, res->error); 439 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);
427 } 440 }
428 free_cmd_results(res); 441 free_cmd_results(res);
429 free(line); 442 free(line);
430 list_del(config->cmd_queue, 0); 443 list_del(config->cmd_queue, 0);
431 } 444 }
445 transaction_commit_dirty();
432 446
433 if (!terminate_request) { 447 if (!terminate_request) {
434 server_run(&server); 448 server_run(&server);
435 } 449 }
436 450
437 wlr_log(L_INFO, "Shutting down sway"); 451 wlr_log(WLR_INFO, "Shutting down sway");
438 452
439 server_fini(&server); 453 server_fini(&server);
440 454
diff --git a/sway/meson.build b/sway/meson.build
index e492aeee..c58d3470 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -35,11 +35,13 @@ sway_sources = files(
35 'commands/border.c', 35 'commands/border.c',
36 'commands/client.c', 36 'commands/client.c',
37 'commands/default_border.c', 37 'commands/default_border.c',
38 'commands/default_floating_border.c',
38 'commands/default_orientation.c', 39 'commands/default_orientation.c',
39 'commands/exit.c', 40 'commands/exit.c',
40 'commands/exec.c', 41 'commands/exec.c',
41 'commands/exec_always.c', 42 'commands/exec_always.c',
42 'commands/floating.c', 43 'commands/floating.c',
44 'commands/floating_minmax_size.c',
43 'commands/focus.c', 45 'commands/focus.c',
44 'commands/focus_follows_mouse.c', 46 'commands/focus_follows_mouse.c',
45 'commands/focus_wrapping.c', 47 'commands/focus_wrapping.c',
@@ -58,6 +60,7 @@ sway_sources = files(
58 'commands/mode.c', 60 'commands/mode.c',
59 'commands/mouse_warping.c', 61 'commands/mouse_warping.c',
60 'commands/move.c', 62 'commands/move.c',
63 'commands/no_focus.c',
61 'commands/output.c', 64 'commands/output.c',
62 'commands/reload.c', 65 'commands/reload.c',
63 'commands/rename.c', 66 'commands/rename.c',
@@ -75,6 +78,7 @@ sway_sources = files(
75 'commands/swap.c', 78 'commands/swap.c',
76 'commands/title_format.c', 79 'commands/title_format.c',
77 'commands/unmark.c', 80 'commands/unmark.c',
81 'commands/urgent.c',
78 'commands/workspace.c', 82 'commands/workspace.c',
79 'commands/workspace_layout.c', 83 'commands/workspace_layout.c',
80 'commands/ws_auto_back_and_forth.c', 84 'commands/ws_auto_back_and_forth.c',
@@ -117,8 +121,10 @@ sway_sources = files(
117 'commands/input/pointer_accel.c', 121 'commands/input/pointer_accel.c',
118 'commands/input/repeat_delay.c', 122 'commands/input/repeat_delay.c',
119 'commands/input/repeat_rate.c', 123 'commands/input/repeat_rate.c',
124 'commands/input/scroll_button.c',
120 'commands/input/scroll_method.c', 125 'commands/input/scroll_method.c',
121 'commands/input/tap.c', 126 'commands/input/tap.c',
127 'commands/input/tap_button_map.c',
122 'commands/input/xkb_layout.c', 128 'commands/input/xkb_layout.c',
123 'commands/input/xkb_model.c', 129 'commands/input/xkb_model.c',
124 'commands/input/xkb_options.c', 130 'commands/input/xkb_options.c',
diff --git a/sway/server.c b/sway/server.c
index cd15f454..f904b177 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -14,7 +14,6 @@
14#include <wlr/types/wlr_linux_dmabuf.h> 14#include <wlr/types/wlr_linux_dmabuf.h>
15#include <wlr/types/wlr_primary_selection.h> 15#include <wlr/types/wlr_primary_selection.h>
16#include <wlr/types/wlr_screencopy_v1.h> 16#include <wlr/types/wlr_screencopy_v1.h>
17#include <wlr/types/wlr_screenshooter.h>
18#include <wlr/types/wlr_server_decoration.h> 17#include <wlr/types/wlr_server_decoration.h>
19#include <wlr/types/wlr_xcursor_manager.h> 18#include <wlr/types/wlr_xcursor_manager.h>
20#include <wlr/types/wlr_xdg_output.h> 19#include <wlr/types/wlr_xdg_output.h>
@@ -29,20 +28,20 @@
29#include "sway/xwayland.h" 28#include "sway/xwayland.h"
30 29
31bool server_privileged_prepare(struct sway_server *server) { 30bool server_privileged_prepare(struct sway_server *server) {
32 wlr_log(L_DEBUG, "Preparing Wayland server initialization"); 31 wlr_log(WLR_DEBUG, "Preparing Wayland server initialization");
33 server->wl_display = wl_display_create(); 32 server->wl_display = wl_display_create();
34 server->wl_event_loop = wl_display_get_event_loop(server->wl_display); 33 server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
35 server->backend = wlr_backend_autocreate(server->wl_display, NULL); 34 server->backend = wlr_backend_autocreate(server->wl_display, NULL);
36 35
37 if (!server->backend) { 36 if (!server->backend) {
38 wlr_log(L_ERROR, "Unable to create backend"); 37 wlr_log(WLR_ERROR, "Unable to create backend");
39 return false; 38 return false;
40 } 39 }
41 return true; 40 return true;
42} 41}
43 42
44bool server_init(struct sway_server *server) { 43bool server_init(struct sway_server *server) {
45 wlr_log(L_DEBUG, "Initializing Wayland server"); 44 wlr_log(WLR_DEBUG, "Initializing Wayland server");
46 45
47 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend); 46 struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);
48 assert(renderer); 47 assert(renderer);
@@ -53,7 +52,6 @@ bool server_init(struct sway_server *server) {
53 server->data_device_manager = 52 server->data_device_manager =
54 wlr_data_device_manager_create(server->wl_display); 53 wlr_data_device_manager_create(server->wl_display);
55 54
56 wlr_screenshooter_create(server->wl_display);
57 wlr_gamma_control_manager_create(server->wl_display); 55 wlr_gamma_control_manager_create(server->wl_display);
58 wlr_primary_selection_device_manager_create(server->wl_display); 56 wlr_primary_selection_device_manager_create(server->wl_display);
59 57
@@ -116,7 +114,7 @@ bool server_init(struct sway_server *server) {
116 114
117 server->socket = wl_display_add_socket_auto(server->wl_display); 115 server->socket = wl_display_add_socket_auto(server->wl_display);
118 if (!server->socket) { 116 if (!server->socket) {
119 wlr_log(L_ERROR, "Unable to open wayland socket"); 117 wlr_log(WLR_ERROR, "Unable to open wayland socket");
120 wlr_backend_destroy(server->backend); 118 wlr_backend_destroy(server->backend);
121 return false; 119 return false;
122 } 120 }
@@ -125,8 +123,7 @@ bool server_init(struct sway_server *server) {
125 if (debug != NULL && strcmp(debug, "txn_timings") == 0) { 123 if (debug != NULL && strcmp(debug, "txn_timings") == 0) {
126 server->debug_txn_timings = true; 124 server->debug_txn_timings = true;
127 } 125 }
128 server->destroying_containers = create_list(); 126 server->dirty_containers = create_list();
129
130 server->transactions = create_list(); 127 server->transactions = create_list();
131 128
132 input_manager = input_manager_create(server); 129 input_manager = input_manager_create(server);
@@ -136,15 +133,15 @@ bool server_init(struct sway_server *server) {
136void server_fini(struct sway_server *server) { 133void server_fini(struct sway_server *server) {
137 // TODO: free sway-specific resources 134 // TODO: free sway-specific resources
138 wl_display_destroy(server->wl_display); 135 wl_display_destroy(server->wl_display);
139 list_free(server->destroying_containers); 136 list_free(server->dirty_containers);
140 list_free(server->transactions); 137 list_free(server->transactions);
141} 138}
142 139
143void server_run(struct sway_server *server) { 140void server_run(struct sway_server *server) {
144 wlr_log(L_INFO, "Running compositor on wayland display '%s'", 141 wlr_log(WLR_INFO, "Running compositor on wayland display '%s'",
145 server->socket); 142 server->socket);
146 if (!wlr_backend_start(server->backend)) { 143 if (!wlr_backend_start(server->backend)) {
147 wlr_log(L_ERROR, "Failed to start backend"); 144 wlr_log(WLR_ERROR, "Failed to start backend");
148 wlr_backend_destroy(server->backend); 145 wlr_backend_destroy(server->backend);
149 return; 146 return;
150 } 147 }
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index cf7a6385..b6391431 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -92,9 +92,20 @@ For more information on these xkb configuration options, see
92*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down 92*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down
93 Changes the scroll method for the specified input device. 93 Changes the scroll method for the specified input device.
94 94
95*input* <identifier> scroll\_button <button\_identifier>
96 Sets button used for scroll\_method on\_button\_down. The button identifier
97 can be obtained from `libinput debug-events`.
98 If set to 0, it disables the scroll\_button on\_button\_down.
99
95*input* <identifier> tap enabled|disabled 100*input* <identifier> tap enabled|disabled
96 Enables or disables tap for specified input device. 101 Enables or disables tap for specified input device.
97 102
103*input* <identifier> tap_button_map lrm|lmr
104 Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as
105 left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_
106 treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as
107 right click.
108
98## SEAT CONFIGURATION 109## SEAT CONFIGURATION
99 110
100Configure options for multiseat mode. sway-seat commands must be used inside a 111Configure options for multiseat mode. sway-seat commands must be used inside a
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 7c553d3f..d369d7b6 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -92,6 +92,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
92*focus* output <name> 92*focus* output <name>
93 Moves focus to the named output. 93 Moves focus to the named output.
94 94
95*focus tiling*
96 Sets focus to the last focused tiling container.
97
98*focus floating*
99 Sets focus to the last focused floating container.
100
95*focus* mode\_toggle 101*focus* mode\_toggle
96 Moves focus between the floating and tiled layers. 102 Moves focus between the floating and tiled layers.
97 103
@@ -493,6 +499,11 @@ config after the others, or it will be matched instead of the others.
493 *unmark* will remove _identifier_ from the list of current marks on a 499 *unmark* will remove _identifier_ from the list of current marks on a
494 window. If _identifier_ is omitted, all marks are removed. 500 window. If _identifier_ is omitted, all marks are removed.
495 501
502*urgent* enable|disable|allow|deny
503 Using _enable_ or _disable_ manually sets or unsets the window's urgent
504 state. Using _allow_ or _deny_ controls the window's ability to set itself
505 as urgent. By default, windows are allowed to set their own urgency.
506
496*workspace* [number] <name> 507*workspace* [number] <name>
497 Switches to the specified workspace. The string "number" is optional and is 508 Switches to the specified workspace. The string "number" is optional and is
498 used to sort workspaces. 509 used to sort workspaces.
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 582b2891..533cf71c 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -47,11 +47,11 @@ static void apply_horiz_layout(struct sway_container *parent) {
47 double scale = parent->width / total_width; 47 double scale = parent->width / total_width;
48 48
49 // Resize windows 49 // Resize windows
50 wlr_log(L_DEBUG, "Arranging %p horizontally", parent); 50 wlr_log(WLR_DEBUG, "Arranging %p horizontally", parent);
51 double child_x = parent->x; 51 double child_x = parent->x;
52 for (size_t i = 0; i < num_children; ++i) { 52 for (size_t i = 0; i < num_children; ++i) {
53 struct sway_container *child = parent->children->items[i]; 53 struct sway_container *child = parent->children->items[i];
54 wlr_log(L_DEBUG, 54 wlr_log(WLR_DEBUG,
55 "Calculating arrangement for %p:%d (will scale %f by %f)", 55 "Calculating arrangement for %p:%d (will scale %f by %f)",
56 child, child->type, child->width, scale); 56 child, child->type, child->width, scale);
57 child->x = child_x; 57 child->x = child_x;
@@ -99,11 +99,11 @@ static void apply_vert_layout(struct sway_container *parent) {
99 double scale = parent_height / total_height; 99 double scale = parent_height / total_height;
100 100
101 // Resize 101 // Resize
102 wlr_log(L_DEBUG, "Arranging %p vertically", parent); 102 wlr_log(WLR_DEBUG, "Arranging %p vertically", parent);
103 double child_y = parent->y + parent_offset; 103 double child_y = parent->y + parent_offset;
104 for (size_t i = 0; i < num_children; ++i) { 104 for (size_t i = 0; i < num_children; ++i) {
105 struct sway_container *child = parent->children->items[i]; 105 struct sway_container *child = parent->children->items[i];
106 wlr_log(L_DEBUG, 106 wlr_log(WLR_DEBUG,
107 "Calculating arrangement for %p:%d (will scale %f by %f)", 107 "Calculating arrangement for %p:%d (will scale %f by %f)",
108 child, child->type, child->height, scale); 108 child, child->type, child->height, scale);
109 child->x = parent->x; 109 child->x = parent->x;
@@ -144,42 +144,26 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
144 } 144 }
145} 145}
146 146
147/** 147static void arrange_children_of(struct sway_container *parent);
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
160static void arrange_children_of(struct sway_container *parent,
161 struct sway_transaction *transaction);
162 148
163static void arrange_floating(struct sway_container *floating, 149static void arrange_floating(struct sway_container *floating) {
164 struct sway_transaction *transaction) {
165 for (int i = 0; i < floating->children->length; ++i) { 150 for (int i = 0; i < floating->children->length; ++i) {
166 struct sway_container *floater = floating->children->items[i]; 151 struct sway_container *floater = floating->children->items[i];
167 if (floater->type == C_VIEW) { 152 if (floater->type == C_VIEW) {
168 view_autoconfigure(floater->sway_view); 153 view_autoconfigure(floater->sway_view);
169 } else { 154 } else {
170 arrange_children_of(floater, transaction); 155 arrange_children_of(floater);
171 } 156 }
172 transaction_add_container(transaction, floater); 157 container_set_dirty(floater);
173 } 158 }
174 transaction_add_container(transaction, floating); 159 container_set_dirty(floating);
175} 160}
176 161
177static void arrange_children_of(struct sway_container *parent, 162static void arrange_children_of(struct sway_container *parent) {
178 struct sway_transaction *transaction) {
179 if (config->reloading) { 163 if (config->reloading) {
180 return; 164 return;
181 } 165 }
182 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, 166 wlr_log(WLR_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent,
183 parent->name, parent->width, parent->height, parent->x, parent->y); 167 parent->name, parent->width, parent->height, parent->x, parent->y);
184 168
185 // Calculate x, y, width and height of children 169 // Calculate x, y, width and height of children
@@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent,
198 apply_horiz_layout(parent); 182 apply_horiz_layout(parent);
199 break; 183 break;
200 case L_FLOATING: 184 case L_FLOATING:
201 arrange_floating(parent, transaction); 185 arrange_floating(parent);
202 break; 186 break;
203 } 187 }
204 188
@@ -213,20 +197,19 @@ static void arrange_children_of(struct sway_container *parent,
213 if (child->type == C_VIEW) { 197 if (child->type == C_VIEW) {
214 view_autoconfigure(child->sway_view); 198 view_autoconfigure(child->sway_view);
215 } else { 199 } else {
216 arrange_children_of(child, transaction); 200 arrange_children_of(child);
217 } 201 }
218 transaction_add_container(transaction, child); 202 container_set_dirty(child);
219 } 203 }
220} 204}
221 205
222static void arrange_workspace(struct sway_container *workspace, 206static void arrange_workspace(struct sway_container *workspace) {
223 struct sway_transaction *transaction) {
224 if (config->reloading) { 207 if (config->reloading) {
225 return; 208 return;
226 } 209 }
227 struct sway_container *output = workspace->parent; 210 struct sway_container *output = workspace->parent;
228 struct wlr_box *area = &output->sway_output->usable_area; 211 struct wlr_box *area = &output->sway_output->usable_area;
229 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", 212 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
230 area->width, area->height, area->x, area->y); 213 area->width, area->height, area->x, area->y);
231 remove_gaps(workspace); 214 remove_gaps(workspace);
232 workspace->width = area->width; 215 workspace->width = area->width;
@@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace,
234 workspace->x = output->x + area->x; 217 workspace->x = output->x + area->x;
235 workspace->y = output->y + area->y; 218 workspace->y = output->y + area->y;
236 add_gaps(workspace); 219 add_gaps(workspace);
237 transaction_add_container(transaction, workspace); 220 container_set_dirty(workspace);
238 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 221 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
239 workspace->x, workspace->y); 222 workspace->x, workspace->y);
240 arrange_floating(workspace->sway_workspace->floating, transaction); 223 arrange_floating(workspace->sway_workspace->floating);
241 arrange_children_of(workspace, transaction); 224 arrange_children_of(workspace);
242} 225}
243 226
244static void arrange_output(struct sway_container *output, 227static void arrange_output(struct sway_container *output) {
245 struct sway_transaction *transaction) {
246 if (config->reloading) { 228 if (config->reloading) {
247 return; 229 return;
248 } 230 }
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,
253 output->y = output_box->y; 235 output->y = output_box->y;
254 output->width = output_box->width; 236 output->width = output_box->width;
255 output->height = output_box->height; 237 output->height = output_box->height;
256 transaction_add_container(transaction, output); 238 container_set_dirty(output);
257 wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f", 239 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
258 output->name, output->x, output->y); 240 output->name, output->x, output->y);
259 for (int i = 0; i < output->children->length; ++i) { 241 for (int i = 0; i < output->children->length; ++i) {
260 struct sway_container *workspace = output->children->items[i]; 242 struct sway_container *workspace = output->children->items[i];
261 arrange_workspace(workspace, transaction); 243 arrange_workspace(workspace);
262 } 244 }
263} 245}
264 246
265static void arrange_root(struct sway_transaction *transaction) { 247static void arrange_root() {
266 if (config->reloading) { 248 if (config->reloading) {
267 return; 249 return;
268 } 250 }
@@ -274,48 +256,40 @@ static void arrange_root(struct sway_transaction *transaction) {
274 root_container.y = layout_box->y; 256 root_container.y = layout_box->y;
275 root_container.width = layout_box->width; 257 root_container.width = layout_box->width;
276 root_container.height = layout_box->height; 258 root_container.height = layout_box->height;
277 transaction_add_container(transaction, &root_container); 259 container_set_dirty(&root_container);
278 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root_container.children->length; ++i) {
279 struct sway_container *output = root_container.children->items[i]; 261 struct sway_container *output = root_container.children->items[i];
280 arrange_output(output, transaction); 262 arrange_output(output);
281 } 263 }
282} 264}
283 265
284void arrange_windows(struct sway_container *container, 266void arrange_windows(struct sway_container *container) {
285 struct sway_transaction *transaction) {
286 switch (container->type) { 267 switch (container->type) {
287 case C_ROOT: 268 case C_ROOT:
288 arrange_root(transaction); 269 arrange_root();
289 break; 270 break;
290 case C_OUTPUT: 271 case C_OUTPUT:
291 arrange_output(container, transaction); 272 arrange_output(container);
292 break; 273 break;
293 case C_WORKSPACE: 274 case C_WORKSPACE:
294 arrange_workspace(container, transaction); 275 arrange_workspace(container);
295 break; 276 break;
296 case C_CONTAINER: 277 case C_CONTAINER:
297 arrange_children_of(container, transaction); 278 arrange_children_of(container);
298 transaction_add_container(transaction, container); 279 container_set_dirty(container);
299 break; 280 break;
300 case C_VIEW: 281 case C_VIEW:
301 view_autoconfigure(container->sway_view); 282 view_autoconfigure(container->sway_view);
302 transaction_add_container(transaction, container); 283 container_set_dirty(container);
303 break; 284 break;
304 case C_TYPES: 285 case C_TYPES:
305 break; 286 break;
306 } 287 }
307 add_deleted_containers(transaction);
308}
309
310void arrange_and_commit(struct sway_container *container) {
311 struct sway_transaction *transaction = transaction_create();
312 arrange_windows(container, transaction);
313 transaction_commit(transaction);
314} 288}
315 289
316void remove_gaps(struct sway_container *c) { 290void remove_gaps(struct sway_container *c) {
317 if (c->current_gaps == 0) { 291 if (c->current_gaps == 0) {
318 wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c); 292 wlr_log(WLR_DEBUG, "Removing gaps: not gapped: %p", c);
319 return; 293 return;
320 } 294 }
321 295
@@ -326,12 +300,12 @@ void remove_gaps(struct sway_container *c) {
326 300
327 c->current_gaps = 0; 301 c->current_gaps = 0;
328 302
329 wlr_log(L_DEBUG, "Removing gaps %p", c); 303 wlr_log(WLR_DEBUG, "Removing gaps %p", c);
330} 304}
331 305
332void add_gaps(struct sway_container *c) { 306void add_gaps(struct sway_container *c) {
333 if (c->current_gaps > 0 || c->type == C_CONTAINER) { 307 if (c->current_gaps > 0 || c->type == C_CONTAINER) {
334 wlr_log(L_DEBUG, "Not adding gaps: %p", c); 308 wlr_log(WLR_DEBUG, "Not adding gaps: %p", c);
335 return; 309 return;
336 } 310 }
337 311
@@ -348,5 +322,5 @@ void add_gaps(struct sway_container *c) {
348 c->height -= 2 * gaps; 322 c->height -= 2 * gaps;
349 c->current_gaps = gaps; 323 c->current_gaps = gaps;
350 324
351 wlr_log(L_DEBUG, "Adding gaps: %p", c); 325 wlr_log(WLR_DEBUG, "Adding gaps: %p", c);
352} 326}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 7cea43fa..02384199 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -11,11 +11,14 @@
11#include "cairo.h" 11#include "cairo.h"
12#include "pango.h" 12#include "pango.h"
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/desktop.h"
15#include "sway/desktop/transaction.h"
14#include "sway/input/input-manager.h" 16#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 17#include "sway/input/seat.h"
16#include "sway/ipc-server.h" 18#include "sway/ipc-server.h"
17#include "sway/output.h" 19#include "sway/output.h"
18#include "sway/server.h" 20#include "sway/server.h"
21#include "sway/tree/arrange.h"
19#include "sway/tree/layout.h" 22#include "sway/tree/layout.h"
20#include "sway/tree/view.h" 23#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 24#include "sway/tree/workspace.h"
@@ -28,7 +31,7 @@ static list_t *get_bfs_queue() {
28 if (!bfs_queue) { 31 if (!bfs_queue) {
29 bfs_queue = create_list(); 32 bfs_queue = create_list();
30 if (!bfs_queue) { 33 if (!bfs_queue) {
31 wlr_log(L_ERROR, "could not allocate list for bfs queue"); 34 wlr_log(WLR_ERROR, "could not allocate list for bfs queue");
32 return NULL; 35 return NULL;
33 } 36 }
34 } 37 }
@@ -156,14 +159,6 @@ void container_free(struct sway_container *cont) {
156 wlr_texture_destroy(cont->title_focused_inactive); 159 wlr_texture_destroy(cont->title_focused_inactive);
157 wlr_texture_destroy(cont->title_unfocused); 160 wlr_texture_destroy(cont->title_unfocused);
158 wlr_texture_destroy(cont->title_urgent); 161 wlr_texture_destroy(cont->title_urgent);
159
160 for (int i = 0; i < server.destroying_containers->length; ++i) {
161 if (server.destroying_containers->items[i] == cont) {
162 list_del(server.destroying_containers, i);
163 break;
164 }
165 }
166
167 list_free(cont->instructions); 162 list_free(cont->instructions);
168 list_free(cont->children); 163 list_free(cont->children);
169 list_free(cont->current.children); 164 list_free(cont->current.children);
@@ -218,7 +213,7 @@ static struct sway_container *container_workspace_destroy(
218 return NULL; 213 return NULL;
219 } 214 }
220 215
221 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 216 wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name);
222 217
223 if (!workspace_is_empty(workspace)) { 218 if (!workspace_is_empty(workspace)) {
224 // Move children to a different workspace on this output 219 // Move children to a different workspace on this output
@@ -230,7 +225,7 @@ static struct sway_container *container_workspace_destroy(
230 } 225 }
231 } 226 }
232 227
233 wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", 228 wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'",
234 workspace->name, new_workspace->name); 229 workspace->name, new_workspace->name);
235 for (int i = 0; i < workspace->children->length; i++) { 230 for (int i = 0; i < workspace->children->length; i++) {
236 container_move_to(workspace->children->items[i], new_workspace); 231 container_move_to(workspace->children->items[i], new_workspace);
@@ -296,7 +291,7 @@ static struct sway_container *container_output_destroy(
296 output->sway_output->swayc = NULL; 291 output->sway_output->swayc = NULL;
297 output->sway_output = NULL; 292 output->sway_output = NULL;
298 293
299 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 294 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
300 295
301 return &root_container; 296 return &root_container;
302} 297}
@@ -323,13 +318,13 @@ static struct sway_container *container_destroy_noreaping(
323 // Workspaces will refuse to be destroyed if they're the last workspace 318 // Workspaces will refuse to be destroyed if they're the last workspace
324 // on their output. 319 // on their output.
325 if (!container_workspace_destroy(con)) { 320 if (!container_workspace_destroy(con)) {
326 wlr_log(L_ERROR, "workspace doesn't want to destroy"); 321 wlr_log(WLR_ERROR, "workspace doesn't want to destroy");
327 return NULL; 322 return NULL;
328 } 323 }
329 } 324 }
330 325
331 con->destroying = true; 326 con->destroying = true;
332 list_add(server.destroying_containers, con); 327 container_set_dirty(con);
333 328
334 if (!con->parent) { 329 if (!con->parent) {
335 return NULL; 330 return NULL;
@@ -350,7 +345,7 @@ bool container_reap_empty(struct sway_container *con) {
350 break; 345 break;
351 case C_WORKSPACE: 346 case C_WORKSPACE:
352 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 347 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
353 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 348 wlr_log(WLR_DEBUG, "Destroying workspace via reaper");
354 container_destroy_noreaping(con); 349 container_destroy_noreaping(con);
355 return true; 350 return true;
356 } 351 }
@@ -443,7 +438,7 @@ struct sway_container *container_view_create(struct sway_container *sibling,
443 } 438 }
444 const char *title = view_get_title(sway_view); 439 const char *title = view_get_title(sway_view);
445 struct sway_container *swayc = container_create(C_VIEW); 440 struct sway_container *swayc = container_create(C_VIEW);
446 wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", 441 wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",
447 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); 442 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
448 // Setup values 443 // Setup values
449 swayc->sway_view = sway_view; 444 swayc->sway_view = sway_view;
@@ -686,16 +681,23 @@ struct sway_container *floating_container_at(double lx, double ly,
686void container_for_each_descendant_dfs(struct sway_container *container, 681void container_for_each_descendant_dfs(struct sway_container *container,
687 void (*f)(struct sway_container *container, void *data), 682 void (*f)(struct sway_container *container, void *data),
688 void *data) { 683 void *data) {
689 if (container) { 684 if (!container) {
690 if (container->children) { 685 return;
691 for (int i = 0; i < container->children->length; ++i) { 686 }
692 struct sway_container *child = 687 if (container->children) {
693 container->children->items[i]; 688 for (int i = 0; i < container->children->length; ++i) {
694 container_for_each_descendant_dfs(child, f, data); 689 struct sway_container *child = container->children->items[i];
695 } 690 container_for_each_descendant_dfs(child, f, data);
691 }
692 }
693 if (container->type == C_WORKSPACE) {
694 struct sway_container *floating = container->sway_workspace->floating;
695 for (int i = 0; i < floating->children->length; ++i) {
696 struct sway_container *child = floating->children->items[i];
697 container_for_each_descendant_dfs(child, f, data);
696 } 698 }
697 f(container, data);
698 } 699 }
700 f(container, data);
699} 701}
700 702
701void container_for_each_descendant_bfs(struct sway_container *con, 703void container_for_each_descendant_bfs(struct sway_container *con,
@@ -706,7 +708,7 @@ void container_for_each_descendant_bfs(struct sway_container *con,
706 } 708 }
707 709
708 if (queue == NULL) { 710 if (queue == NULL) {
709 wlr_log(L_ERROR, "could not allocate list"); 711 wlr_log(WLR_ERROR, "could not allocate list");
710 return; 712 return;
711 } 713 }
712 714
@@ -972,9 +974,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
972 return; 974 return;
973 } 975 }
974 struct sway_view *view = con->sway_view; 976 struct sway_view *view = con->sway_view;
975 size_t border_width = view->border_thickness * (view->border != B_NONE); 977 size_t border_width = 0;
976 size_t top = 978 size_t top = 0;
977 view->border == B_NORMAL ? container_titlebar_height() : border_width; 979
980 if (!view->using_csd) {
981 border_width = view->border_thickness * (view->border != B_NONE);
982 top = view->border == B_NORMAL ?
983 container_titlebar_height() : border_width;
984 }
978 985
979 con->x = view->x - border_width; 986 con->x = view->x - border_width;
980 con->y = view->y - top; 987 con->y = view->y - top;
@@ -996,3 +1003,103 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
996 box->width = container->width; 1003 box->width = container->width;
997 box->height = container->height; 1004 box->height = container->height;
998} 1005}
1006
1007/**
1008 * Translate the container's position as well as all children.
1009 */
1010static void container_floating_translate(struct sway_container *con,
1011 double x_amount, double y_amount) {
1012 con->x += x_amount;
1013 con->y += y_amount;
1014 con->current.swayc_x += x_amount;
1015 con->current.swayc_y += y_amount;
1016 if (con->type == C_VIEW) {
1017 con->sway_view->x += x_amount;
1018 con->sway_view->y += y_amount;
1019 con->current.view_x += x_amount;
1020 con->current.view_y += y_amount;
1021 } else {
1022 for (int i = 0; i < con->children->length; ++i) {
1023 struct sway_container *child = con->children->items[i];
1024 container_floating_translate(child, x_amount, y_amount);
1025 }
1026 }
1027}
1028
1029/**
1030 * Choose an output for the floating container's new position.
1031 *
1032 * If the center of the container intersects an output then we'll choose that
1033 * one, otherwise we'll choose whichever output is closest to the container's
1034 * center.
1035 */
1036static struct sway_container *container_floating_find_output(
1037 struct sway_container *con) {
1038 double center_x = con->x + con->width / 2;
1039 double center_y = con->y + con->height / 2;
1040 struct sway_container *closest_output = NULL;
1041 double closest_distance = DBL_MAX;
1042 for (int i = 0; i < root_container.children->length; ++i) {
1043 struct sway_container *output = root_container.children->items[i];
1044 struct wlr_box output_box;
1045 double closest_x, closest_y;
1046 container_get_box(output, &output_box);
1047 wlr_box_closest_point(&output_box, center_x, center_y,
1048 &closest_x, &closest_y);
1049 if (center_x == closest_x && center_y == closest_y) {
1050 // The center of the floating container is on this output
1051 return output;
1052 }
1053 double x_dist = closest_x - center_x;
1054 double y_dist = closest_y - center_y;
1055 double distance = x_dist * x_dist + y_dist * y_dist;
1056 if (distance < closest_distance) {
1057 closest_output = output;
1058 closest_distance = distance;
1059 }
1060 }
1061 return closest_output;
1062}
1063
1064void container_floating_move_to(struct sway_container *con,
1065 double lx, double ly) {
1066 if (!sway_assert(container_is_floating(con),
1067 "Expected a floating container")) {
1068 return;
1069 }
1070 desktop_damage_whole_container(con);
1071 container_floating_translate(con, lx - con->x, ly - con->y);
1072 desktop_damage_whole_container(con);
1073 struct sway_container *old_workspace = container_parent(con, C_WORKSPACE);
1074 struct sway_container *new_output = container_floating_find_output(con);
1075 if (!sway_assert(new_output, "Unable to find any output")) {
1076 return;
1077 }
1078 struct sway_container *new_workspace =
1079 output_get_active_workspace(new_output->sway_output);
1080 if (old_workspace != new_workspace) {
1081 container_remove_child(con);
1082 container_add_child(new_workspace->sway_workspace->floating, con);
1083 arrange_windows(old_workspace);
1084 arrange_windows(new_workspace);
1085 workspace_detect_urgent(old_workspace);
1086 workspace_detect_urgent(new_workspace);
1087 }
1088}
1089
1090void container_set_dirty(struct sway_container *container) {
1091 if (container->dirty) {
1092 return;
1093 }
1094 container->dirty = true;
1095 list_add(server.dirty_containers, container);
1096}
1097
1098static bool find_urgent_iterator(struct sway_container *con,
1099 void *data) {
1100 return con->type == C_VIEW && view_is_urgent(con->sway_view);
1101}
1102
1103bool container_has_urgent_child(struct sway_container *container) {
1104 return container_find(container, find_urgent_iterator, NULL);
1105}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 14631ad4..197a2fc8 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -22,7 +22,8 @@ struct sway_container root_container;
22 22
23static void output_layout_handle_change(struct wl_listener *listener, 23static void output_layout_handle_change(struct wl_listener *listener,
24 void *data) { 24 void *data) {
25 arrange_and_commit(&root_container); 25 arrange_windows(&root_container);
26 transaction_commit_dirty();
26} 27}
27 28
28void layout_init(void) { 29void layout_init(void) {
@@ -101,7 +102,7 @@ void container_insert_child(struct sway_container *parent,
101 if (old_parent) { 102 if (old_parent) {
102 container_remove_child(child); 103 container_remove_child(child);
103 } 104 }
104 wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); 105 wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);
105 list_insert(parent->children, i, child); 106 list_insert(parent->children, i, child);
106 child->parent = parent; 107 child->parent = parent;
107 container_handle_fullscreen_reparent(child, old_parent); 108 container_handle_fullscreen_reparent(child, old_parent);
@@ -127,7 +128,7 @@ struct sway_container *container_add_sibling(struct sway_container *fixed,
127 128
128void container_add_child(struct sway_container *parent, 129void container_add_child(struct sway_container *parent,
129 struct sway_container *child) { 130 struct sway_container *child) {
130 wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", 131 wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
131 child, child->type, child->width, child->height, 132 child, child->type, child->width, child->height,
132 parent, parent->type, parent->width, parent->height); 133 parent, parent->type, parent->width, parent->height);
133 struct sway_container *old_parent = child->parent; 134 struct sway_container *old_parent = child->parent;
@@ -168,7 +169,12 @@ void container_move_to(struct sway_container *container,
168 struct sway_container *old_parent = container_remove_child(container); 169 struct sway_container *old_parent = container_remove_child(container);
169 container->width = container->height = 0; 170 container->width = container->height = 0;
170 container->saved_width = container->saved_height = 0; 171 container->saved_width = container->saved_height = 0;
171 struct sway_container *new_parent; 172
173 struct sway_container *new_parent, *new_parent_focus;
174 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
175
176 // Get the focus of the destination before we change it.
177 new_parent_focus = seat_get_focus_inactive(seat, destination);
172 if (destination->type == C_VIEW) { 178 if (destination->type == C_VIEW) {
173 new_parent = container_add_sibling(destination, container); 179 new_parent = container_add_sibling(destination, container);
174 } else { 180 } else {
@@ -176,17 +182,20 @@ void container_move_to(struct sway_container *container,
176 container_add_child(destination, container); 182 container_add_child(destination, container);
177 } 183 }
178 wl_signal_emit(&container->events.reparent, old_parent); 184 wl_signal_emit(&container->events.reparent, old_parent);
185
179 if (container->type == C_WORKSPACE) { 186 if (container->type == C_WORKSPACE) {
180 // If moving a workspace to a new output, maybe create a new workspace 187 // If moving a workspace to a new output, maybe create a new workspace
181 // on the previous output 188 // on the previous output
182 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
183 if (old_parent->children->length == 0) { 189 if (old_parent->children->length == 0) {
184 char *ws_name = workspace_next_name(old_parent->name); 190 char *ws_name = workspace_next_name(old_parent->name);
185 struct sway_container *ws = 191 struct sway_container *ws = workspace_create(old_parent, ws_name);
186 workspace_create(old_parent, ws_name);
187 free(ws_name); 192 free(ws_name);
188 seat_set_focus(seat, ws); 193 seat_set_focus(seat, ws);
189 } 194 }
195
196 // Try to remove an empty workspace from the destination output.
197 container_reap_empty_recursive(new_parent_focus);
198
190 container_sort_workspaces(new_parent); 199 container_sort_workspaces(new_parent);
191 seat_set_focus(seat, new_parent); 200 seat_set_focus(seat, new_parent);
192 workspace_output_raise_priority(container, old_parent, new_parent); 201 workspace_output_raise_priority(container, old_parent, new_parent);
@@ -216,6 +225,15 @@ void container_move_to(struct sway_container *container,
216 } 225 }
217 } 226 }
218 } 227 }
228 // Update workspace urgent state
229 struct sway_container *old_workspace = old_parent;
230 if (old_workspace->type != C_WORKSPACE) {
231 old_workspace = container_parent(old_workspace, C_WORKSPACE);
232 }
233 if (new_workspace != old_workspace) {
234 workspace_detect_urgent(new_workspace);
235 workspace_detect_urgent(old_workspace);
236 }
219} 237}
220 238
221static bool sway_dir_to_wlr(enum movement_direction dir, 239static bool sway_dir_to_wlr(enum movement_direction dir,
@@ -311,13 +329,13 @@ static void move_out_of_tabs_stacks(struct sway_container *container,
311 int offs) { 329 int offs) {
312 if (container->parent == current->parent 330 if (container->parent == current->parent
313 && current->parent->children->length == 1) { 331 && current->parent->children->length == 1) {
314 wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); 332 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id);
315 current->parent->layout = move_dir == 333 current->parent->layout = move_dir ==
316 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 334 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
317 return; 335 return;
318 } 336 }
319 337
320 wlr_log(L_DEBUG, "Moving out of tab/stack into a split"); 338 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
321 bool is_workspace = current->parent->type == C_WORKSPACE; 339 bool is_workspace = current->parent->type == C_WORKSPACE;
322 struct sway_container *new_parent = container_split(current->parent, 340 struct sway_container *new_parent = container_split(current->parent,
323 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); 341 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT);
@@ -362,7 +380,7 @@ void container_move(struct sway_container *container,
362 } 380 }
363 381
364 parent = current->parent; 382 parent = current->parent;
365 wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, 383 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current,
366 container_type_to_str(current->type), current->name); 384 container_type_to_str(current->type), current->name);
367 385
368 int index = index_child(current); 386 int index = index_child(current);
@@ -380,12 +398,12 @@ void container_move(struct sway_container *container,
380 root_container.sway_root->output_layout, wlr_dir, 398 root_container.sway_root->output_layout, wlr_dir,
381 current->sway_output->wlr_output, ref_lx, ref_ly); 399 current->sway_output->wlr_output, ref_lx, ref_ly);
382 if (!next) { 400 if (!next) {
383 wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); 401 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
384 return; 402 return;
385 } 403 }
386 struct sway_output *next_output = next->data; 404 struct sway_output *next_output = next->data;
387 current = next_output->swayc; 405 current = next_output->swayc;
388 wlr_log(L_DEBUG, "Selected next output (%s)", current->name); 406 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
389 // Select workspace and get outta here 407 // Select workspace and get outta here
390 current = seat_get_focus_inactive( 408 current = seat_get_focus_inactive(
391 config->handler_context.seat, current); 409 config->handler_context.seat, current);
@@ -398,20 +416,20 @@ void container_move(struct sway_container *container,
398 case C_WORKSPACE: 416 case C_WORKSPACE:
399 if (!is_parallel(current->layout, move_dir)) { 417 if (!is_parallel(current->layout, move_dir)) {
400 if (current->children->length >= 2) { 418 if (current->children->length >= 2) {
401 wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", 419 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
402 current->children->length); 420 current->children->length);
403 workspace_rejigger(current, container, move_dir); 421 workspace_rejigger(current, container, move_dir);
404 return; 422 return;
405 } else { 423 } else {
406 wlr_log(L_DEBUG, "Selecting output"); 424 wlr_log(WLR_DEBUG, "Selecting output");
407 current = current->parent; 425 current = current->parent;
408 } 426 }
409 } else if (current->layout == L_TABBED 427 } else if (current->layout == L_TABBED
410 || current->layout == L_STACKED) { 428 || current->layout == L_STACKED) {
411 wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); 429 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
412 workspace_rejigger(current, container, move_dir); 430 workspace_rejigger(current, container, move_dir);
413 } else { 431 } else {
414 wlr_log(L_DEBUG, "Selecting output"); 432 wlr_log(WLR_DEBUG, "Selecting output");
415 current = current->parent; 433 current = current->parent;
416 } 434 }
417 break; 435 break;
@@ -427,11 +445,11 @@ void container_move(struct sway_container *container,
427 move_dir, offs); 445 move_dir, offs);
428 return; 446 return;
429 } else { 447 } else {
430 wlr_log(L_DEBUG, "Hit limit, selecting parent"); 448 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
431 current = current->parent; 449 current = current->parent;
432 } 450 }
433 } else { 451 } else {
434 wlr_log(L_DEBUG, "Hit limit, " 452 wlr_log(WLR_DEBUG, "Hit limit, "
435 "promoting descendant to sibling"); 453 "promoting descendant to sibling");
436 // Special case 454 // Special case
437 container_insert_child(current->parent, container, 455 container_insert_child(current->parent, container,
@@ -441,14 +459,14 @@ void container_move(struct sway_container *container,
441 } 459 }
442 } else { 460 } else {
443 sibling = parent->children->items[index + offs]; 461 sibling = parent->children->items[index + offs];
444 wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); 462 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
445 } 463 }
446 } else if (parent->layout == L_TABBED 464 } else if (parent->layout == L_TABBED
447 || parent->layout == L_STACKED) { 465 || parent->layout == L_STACKED) {
448 move_out_of_tabs_stacks(container, current, move_dir, offs); 466 move_out_of_tabs_stacks(container, current, move_dir, offs);
449 return; 467 return;
450 } else { 468 } else {
451 wlr_log(L_DEBUG, "Moving up to find a parallel container"); 469 wlr_log(WLR_DEBUG, "Moving up to find a parallel container");
452 current = current->parent; 470 current = current->parent;
453 } 471 }
454 break; 472 break;
@@ -467,11 +485,11 @@ void container_move(struct sway_container *container,
467 switch (sibling->type) { 485 switch (sibling->type) {
468 case C_VIEW: 486 case C_VIEW:
469 if (sibling->parent == container->parent) { 487 if (sibling->parent == container->parent) {
470 wlr_log(L_DEBUG, "Swapping siblings"); 488 wlr_log(WLR_DEBUG, "Swapping siblings");
471 sibling->parent->children->items[index + offs] = container; 489 sibling->parent->children->items[index + offs] = container;
472 sibling->parent->children->items[index] = sibling; 490 sibling->parent->children->items[index] = sibling;
473 } else { 491 } else {
474 wlr_log(L_DEBUG, "Promoting to sibling of cousin"); 492 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
475 container_insert_child(sibling->parent, container, 493 container_insert_child(sibling->parent, container,
476 index_child(sibling) + (offs > 0 ? 0 : 1)); 494 index_child(sibling) + (offs > 0 ? 0 : 1));
477 container->width = container->height = 0; 495 container->width = container->height = 0;
@@ -482,31 +500,31 @@ void container_move(struct sway_container *container,
482 case C_CONTAINER: 500 case C_CONTAINER:
483 if (is_parallel(sibling->layout, move_dir)) { 501 if (is_parallel(sibling->layout, move_dir)) {
484 int limit = container_limit(sibling, invert_movement(move_dir)); 502 int limit = container_limit(sibling, invert_movement(move_dir));
485 wlr_log(L_DEBUG, "limit: %d", limit); 503 wlr_log(WLR_DEBUG, "limit: %d", limit);
486 wlr_log(L_DEBUG, 504 wlr_log(WLR_DEBUG,
487 "Reparenting container (parallel) to index %d " 505 "Reparenting container (parallel) to index %d "
488 "(move dir: %d)", limit, move_dir); 506 "(move dir: %d)", limit, move_dir);
489 container_insert_child(sibling, container, limit); 507 container_insert_child(sibling, container, limit);
490 container->width = container->height = 0; 508 container->width = container->height = 0;
491 sibling = NULL; 509 sibling = NULL;
492 } else { 510 } else {
493 wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); 511 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
494 struct sway_container *focus_inactive = seat_get_focus_inactive( 512 struct sway_container *focus_inactive = seat_get_focus_inactive(
495 config->handler_context.seat, sibling); 513 config->handler_context.seat, sibling);
496 if (focus_inactive && focus_inactive != sibling) { 514 if (focus_inactive && focus_inactive != sibling) {
497 while (focus_inactive->parent != sibling) { 515 while (focus_inactive->parent != sibling) {
498 focus_inactive = focus_inactive->parent; 516 focus_inactive = focus_inactive->parent;
499 } 517 }
500 wlr_log(L_DEBUG, "Focus inactive: id:%zd", 518 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
501 focus_inactive->id); 519 focus_inactive->id);
502 sibling = focus_inactive; 520 sibling = focus_inactive;
503 continue; 521 continue;
504 } else if (sibling->children->length) { 522 } else if (sibling->children->length) {
505 wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); 523 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
506 container_remove_child(container); 524 container_remove_child(container);
507 container_add_sibling(sibling->children->items[0], container); 525 container_add_sibling(sibling->children->items[0], container);
508 } else { 526 } else {
509 wlr_log(L_DEBUG, "No kiddos, adding container alone"); 527 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
510 container_remove_child(container); 528 container_remove_child(container);
511 container_add_child(sibling, container); 529 container_add_child(sibling, container);
512 } 530 }
@@ -539,6 +557,8 @@ void container_move(struct sway_container *container,
539 } 557 }
540 if (last_ws && next_ws && last_ws != next_ws) { 558 if (last_ws && next_ws && last_ws != next_ws) {
541 ipc_event_workspace(last_ws, container, "focus"); 559 ipc_event_workspace(last_ws, container, "focus");
560 workspace_detect_urgent(last_ws);
561 workspace_detect_urgent(next_ws);
542 } 562 }
543} 563}
544 564
@@ -603,7 +623,7 @@ static struct sway_container *get_swayc_in_output_direction(
603 } 623 }
604 624
605 if (ws == NULL) { 625 if (ws == NULL) {
606 wlr_log(L_ERROR, "got an output without a workspace"); 626 wlr_log(WLR_ERROR, "got an output without a workspace");
607 return NULL; 627 return NULL;
608 } 628 }
609 629
@@ -775,7 +795,7 @@ struct sway_container *container_get_in_direction(
775 } else { 795 } else {
776 struct sway_container *desired_con = 796 struct sway_container *desired_con =
777 parent->children->items[desired]; 797 parent->children->items[desired];
778 wlr_log(L_DEBUG, 798 wlr_log(WLR_DEBUG,
779 "cont %d-%p dir %i sibling %d: %p", idx, 799 "cont %d-%p dir %i sibling %d: %p", idx,
780 container, dir, desired, desired_con); 800 container, dir, desired, desired_con);
781 return seat_get_focus_inactive_view(seat, desired_con); 801 return seat_get_focus_inactive_view(seat, desired_con);
@@ -840,7 +860,7 @@ struct sway_container *container_split(struct sway_container *child,
840 860
841 struct sway_container *cont = container_create(C_CONTAINER); 861 struct sway_container *cont = container_create(C_CONTAINER);
842 862
843 wlr_log(L_DEBUG, "creating container %p around %p", cont, child); 863 wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);
844 864
845 remove_gaps(child); 865 remove_gaps(child);
846 866
@@ -888,7 +908,7 @@ struct sway_container *container_split(struct sway_container *child,
888void container_recursive_resize(struct sway_container *container, 908void container_recursive_resize(struct sway_container *container,
889 double amount, enum resize_edge edge) { 909 double amount, enum resize_edge edge) {
890 bool layout_match = true; 910 bool layout_match = true;
891 wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); 911 wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount);
892 if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { 912 if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) {
893 container->width += amount; 913 container->width += amount;
894 layout_match = container->layout == L_HORIZ; 914 layout_match = container->layout == L_HORIZ;
@@ -978,7 +998,7 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
978 return; 998 return;
979 } 999 }
980 1000
981 wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); 1001 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
982 1002
983 int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; 1003 int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen;
984 int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; 1004 int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen;
diff --git a/sway/tree/output.c b/sway/tree/output.c
index e2927cdb..da535c18 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -43,11 +43,11 @@ struct sway_container *output_create(
43 43
44 if (strcasecmp(name, cur->name) == 0 || 44 if (strcasecmp(name, cur->name) == 0 ||
45 strcasecmp(identifier, cur->name) == 0) { 45 strcasecmp(identifier, cur->name) == 0) {
46 wlr_log(L_DEBUG, "Matched output config for %s", name); 46 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
47 oc = cur; 47 oc = cur;
48 } 48 }
49 if (strcasecmp("*", cur->name) == 0) { 49 if (strcasecmp("*", cur->name) == 0) {
50 wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); 50 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
51 all = cur; 51 all = cur;
52 } 52 }
53 53
@@ -86,7 +86,7 @@ struct sway_container *output_create(
86 if (!output->children->length) { 86 if (!output->children->length) {
87 // Create workspace 87 // Create workspace
88 char *ws_name = workspace_next_name(output->name); 88 char *ws_name = workspace_next_name(output->name);
89 wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); 89 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
90 struct sway_container *ws = workspace_create(output, ws_name); 90 struct sway_container *ws = workspace_create(output, ws_name);
91 // Set each seat's focus if not already set 91 // Set each seat's focus if not already set
92 struct sway_seat *seat = NULL; 92 struct sway_seat *seat = NULL;
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 3ef79fa8..fc31699c 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,
25 view->impl = impl; 25 view->impl = impl;
26 view->executed_criteria = create_list(); 26 view->executed_criteria = create_list();
27 view->marks = create_list(); 27 view->marks = create_list();
28 view->allow_request_urgent = true;
28 wl_signal_init(&view->events.unmap); 29 wl_signal_init(&view->events.unmap);
29} 30}
30 31
@@ -150,12 +151,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
150 151
151void view_init_floating(struct sway_view *view) { 152void view_init_floating(struct sway_view *view) {
152 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 153 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
153 int max_width = ws->width * 0.6666; 154 int min_width, min_height;
154 int max_height = ws->height * 0.6666; 155 int max_width, max_height;
155 view->width = 156
156 view->natural_width > max_width ? max_width : view->natural_width; 157 if (config->floating_minimum_width == -1) { // no minimum
157 view->height = 158 min_width = 0;
158 view->natural_height > max_height ? max_height : view->natural_height; 159 } else if (config->floating_minimum_width == 0) { // automatic
160 min_width = 75;
161 } else {
162 min_width = config->floating_minimum_width;
163 }
164
165 if (config->floating_minimum_height == -1) { // no minimum
166 min_height = 0;
167 } else if (config->floating_minimum_height == 0) { // automatic
168 min_height = 50;
169 } else {
170 min_height = config->floating_minimum_height;
171 }
172
173 if (config->floating_maximum_width == -1) { // no maximum
174 max_width = INT_MAX;
175 } else if (config->floating_maximum_width == 0) { // automatic
176 max_width = ws->width * 0.6666;
177 } else {
178 max_width = config->floating_maximum_width;
179 }
180
181 if (config->floating_maximum_height == -1) { // no maximum
182 max_height = INT_MAX;
183 } else if (config->floating_maximum_height == 0) { // automatic
184 max_height = ws->height * 0.6666;
185 } else {
186 max_height = config->floating_maximum_height;
187 }
188
189 view->width = fmax(min_width, fmin(view->natural_width, max_width));
190 view->height = fmax(min_height, fmin(view->natural_height, max_height));
159 view->x = ws->x + (ws->width - view->width) / 2; 191 view->x = ws->x + (ws->width - view->width) / 2;
160 view->y = ws->y + (ws->height - view->height) / 2; 192 view->y = ws->y + (ws->height - view->height) / 2;
161 193
@@ -284,7 +316,15 @@ void view_set_activated(struct sway_view *view, bool activated) {
284} 316}
285 317
286void view_set_tiled(struct sway_view *view, bool tiled) { 318void view_set_tiled(struct sway_view *view, bool tiled) {
287 view->border = tiled ? config->border : B_NONE; 319 if (!tiled) {
320 view->using_csd = true;
321 if (view->impl->has_client_side_decorations) {
322 view->using_csd = view->impl->has_client_side_decorations(view);
323 }
324 } else {
325 view->using_csd = false;
326 }
327
288 if (view->impl->set_tiled) { 328 if (view->impl->set_tiled) {
289 view->impl->set_tiled(view, tiled); 329 view->impl->set_tiled(view, tiled);
290 } 330 }
@@ -462,27 +502,45 @@ void view_execute_criteria(struct sway_view *view) {
462 list_t *criterias = criteria_for_view(view, CT_COMMAND); 502 list_t *criterias = criteria_for_view(view, CT_COMMAND);
463 for (int i = 0; i < criterias->length; i++) { 503 for (int i = 0; i < criterias->length; i++) {
464 struct criteria *criteria = criterias->items[i]; 504 struct criteria *criteria = criterias->items[i];
465 wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); 505 wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw);
466 if (view_has_executed_criteria(view, criteria)) { 506 if (view_has_executed_criteria(view, criteria)) {
467 wlr_log(L_DEBUG, "Criteria already executed"); 507 wlr_log(WLR_DEBUG, "Criteria already executed");
468 continue; 508 continue;
469 } 509 }
470 wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 510 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
471 criteria->raw, view, criteria->cmdlist); 511 criteria->raw, view, criteria->cmdlist);
512 seat_set_focus(seat, view->swayc);
472 list_add(view->executed_criteria, criteria); 513 list_add(view->executed_criteria, criteria);
473 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 514 struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
474 if (res->status != CMD_SUCCESS) { 515 if (res->status != CMD_SUCCESS) {
475 wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); 516 wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);
476 } 517 }
477 free_cmd_results(res); 518 free_cmd_results(res);
478 // view must be focused for commands to affect it,
479 // so always refocus in-between command lists
480 seat_set_focus(seat, view->swayc);
481 } 519 }
482 list_free(criterias); 520 list_free(criterias);
483 seat_set_focus(seat, prior_focus); 521 seat_set_focus(seat, prior_focus);
484} 522}
485 523
524static bool should_focus(struct sway_view *view) {
525 // If the view is the only one in the focused workspace, it'll get focus
526 // regardless of any no_focus criteria.
527 struct sway_container *parent = view->swayc->parent;
528 struct sway_seat *seat = input_manager_current_seat(input_manager);
529 if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) {
530 size_t num_children = parent->children->length +
531 parent->sway_workspace->floating->children->length;
532 if (num_children == 1) {
533 return true;
534 }
535 }
536
537 // Check no_focus criteria
538 list_t *criterias = criteria_for_view(view, CT_NO_FOCUS);
539 size_t len = criterias->length;
540 list_free(criterias);
541 return len == 0;
542}
543
486void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { 544void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
487 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { 545 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
488 return; 546 return;
@@ -519,8 +577,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
519 577
520 view->surface = wlr_surface; 578 view->surface = wlr_surface;
521 view->swayc = cont; 579 view->swayc = cont;
522 view->border = config->border;
523 view->border_thickness = config->border_thickness;
524 580
525 view_init_subsurfaces(view, wlr_surface); 581 view_init_subsurfaces(view, wlr_surface);
526 wl_signal_add(&wlr_surface->events.new_subsurface, 582 wl_signal_add(&wlr_surface->events.new_subsurface,
@@ -531,14 +587,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
531 view->container_reparent.notify = view_handle_container_reparent; 587 view->container_reparent.notify = view_handle_container_reparent;
532 588
533 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 589 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
590 view->border = config->floating_border;
591 view->border_thickness = config->floating_border_thickness;
534 container_set_floating(view->swayc, true); 592 container_set_floating(view->swayc, true);
535 } else { 593 } else {
594 view->border = config->border;
595 view->border_thickness = config->border_thickness;
536 view_set_tiled(view, true); 596 view_set_tiled(view, true);
537 } 597 }
538 598
539 input_manager_set_focus(input_manager, cont); 599 if (should_focus(view)) {
540 if (workspace) { 600 input_manager_set_focus(input_manager, cont);
541 workspace_switch(workspace); 601 if (workspace) {
602 workspace_switch(workspace);
603 }
542 } 604 }
543 605
544 view_update_title(view, false); 606 view_update_title(view, false);
@@ -554,16 +616,27 @@ void view_unmap(struct sway_view *view) {
554 wl_list_remove(&view->surface_new_subsurface.link); 616 wl_list_remove(&view->surface_new_subsurface.link);
555 wl_list_remove(&view->container_reparent.link); 617 wl_list_remove(&view->container_reparent.link);
556 618
619 if (view->urgent_timer) {
620 wl_event_source_remove(view->urgent_timer);
621 view->urgent_timer = NULL;
622 }
623
624 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
625
626 struct sway_container *parent;
557 if (view->is_fullscreen) { 627 if (view->is_fullscreen) {
558 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
559 ws->sway_workspace->fullscreen = NULL; 628 ws->sway_workspace->fullscreen = NULL;
560 container_destroy(view->swayc); 629 parent = container_destroy(view->swayc);
561 630
562 arrange_and_commit(ws->parent); 631 arrange_windows(ws->parent);
563 } else { 632 } else {
564 struct sway_container *parent = container_destroy(view->swayc); 633 parent = container_destroy(view->swayc);
565 arrange_and_commit(parent); 634 arrange_windows(parent);
635 }
636 if (parent->type >= C_WORKSPACE) { // if the workspace still exists
637 workspace_detect_urgent(ws);
566 } 638 }
639 transaction_commit_dirty();
567 view->surface = NULL; 640 view->surface = NULL;
568} 641}
569 642
@@ -601,7 +674,7 @@ static void view_subsurface_create(struct sway_view *view,
601 struct wlr_subsurface *subsurface) { 674 struct wlr_subsurface *subsurface) {
602 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); 675 struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
603 if (child == NULL) { 676 if (child == NULL) {
604 wlr_log(L_ERROR, "Allocation failed"); 677 wlr_log(WLR_ERROR, "Allocation failed");
605 return; 678 return;
606 } 679 }
607 view_child_init(child, NULL, view, subsurface->surface); 680 view_child_init(child, NULL, view, subsurface->surface);
@@ -721,8 +794,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
721 return NULL; 794 return NULL;
722 } 795 }
723 796
724 wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", 797 const char *role = wlr_surface->role ? wlr_surface->role->name : NULL;
725 wlr_surface->role, wlr_surface); 798 wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p",
799 role, wlr_surface);
726 return NULL; 800 return NULL;
727} 801}
728 802
@@ -789,7 +863,7 @@ static char *escape_title(char *buffer) {
789 char *escaped_title = calloc(length + 1, sizeof(char)); 863 char *escaped_title = calloc(length + 1, sizeof(char));
790 int result = escape_markup_text(buffer, escaped_title, length); 864 int result = escape_markup_text(buffer, escaped_title, length);
791 if (result != length) { 865 if (result != length) {
792 wlr_log(L_ERROR, "Could not escape title: %s", buffer); 866 wlr_log(WLR_ERROR, "Could not escape title: %s", buffer);
793 free(escaped_title); 867 free(escaped_title);
794 return buffer; 868 return buffer;
795 } 869 }
@@ -1010,3 +1084,32 @@ bool view_is_visible(struct sway_view *view) {
1010 } 1084 }
1011 return true; 1085 return true;
1012} 1086}
1087
1088void view_set_urgent(struct sway_view *view, bool enable) {
1089 if (view_is_urgent(view) == enable) {
1090 return;
1091 }
1092 if (enable) {
1093 struct sway_seat *seat = input_manager_current_seat(input_manager);
1094 if (seat_get_focus(seat) == view->swayc) {
1095 return;
1096 }
1097 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1098 } else {
1099 view->urgent = (struct timespec){ 0 };
1100 if (view->urgent_timer) {
1101 wl_event_source_remove(view->urgent_timer);
1102 view->urgent_timer = NULL;
1103 }
1104 }
1105 container_damage_whole(view->swayc);
1106
1107 ipc_event_window(view->swayc, "urgent");
1108
1109 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
1110 workspace_detect_urgent(ws);
1111}
1112
1113bool view_is_urgent(struct sway_view *view) {
1114 return view->urgent.tv_sec || view->urgent.tv_nsec;
1115}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 51f0fcb4..622f01ec 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -11,6 +11,7 @@
11#include "sway/ipc-server.h" 11#include "sway/ipc-server.h"
12#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
13#include "sway/tree/container.h" 13#include "sway/tree/container.h"
14#include "sway/tree/view.h"
14#include "sway/tree/workspace.h" 15#include "sway/tree/workspace.h"
15#include "list.h" 16#include "list.h"
16#include "log.h" 17#include "log.h"
@@ -49,7 +50,7 @@ struct sway_container *workspace_create(struct sway_container *output,
49 output = get_workspace_initial_output(name); 50 output = get_workspace_initial_output(name);
50 } 51 }
51 52
52 wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); 53 wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name);
53 struct sway_container *workspace = container_create(C_WORKSPACE); 54 struct sway_container *workspace = container_create(C_WORKSPACE);
54 55
55 workspace->x = output->x; 56 workspace->x = output->x;
@@ -107,7 +108,7 @@ static bool workspace_valid_on_output(const char *output_name,
107} 108}
108 109
109char *workspace_next_name(const char *output_name) { 110char *workspace_next_name(const char *output_name) {
110 wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", 111 wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s",
111 output_name); 112 output_name);
112 // Scan all workspace bindings to find the next available workspace name, 113 // Scan all workspace bindings to find the next available workspace name,
113 // if none are found/available then default to a number 114 // if none are found/available then default to a number
@@ -135,7 +136,7 @@ char *workspace_next_name(const char *output_name) {
135 while (isspace(*_target)) { 136 while (isspace(*_target)) {
136 memmove(_target, _target+1, strlen(_target+1)); 137 memmove(_target, _target+1, strlen(_target+1));
137 } 138 }
138 wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", 139 wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'",
139 _target); 140 _target);
140 141
141 // Make sure that the command references an actual workspace 142 // Make sure that the command references an actual workspace
@@ -161,7 +162,7 @@ char *workspace_next_name(const char *output_name) {
161 temp[length - 1] = '\0'; 162 temp[length - 1] = '\0';
162 free(_target); 163 free(_target);
163 _target = temp; 164 _target = temp;
164 wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); 165 wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target);
165 166
166 // Make sure the workspace number doesn't already exist 167 // Make sure the workspace number doesn't already exist
167 if (workspace_by_number(_target)) { 168 if (workspace_by_number(_target)) {
@@ -190,7 +191,7 @@ char *workspace_next_name(const char *output_name) {
190 order = binding->order; 191 order = binding->order;
191 free(target); 192 free(target);
192 target = _target; 193 target = _target;
193 wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); 194 wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target);
194 } else { 195 } else {
195 free(_target); 196 free(_target);
196 } 197 }
@@ -387,7 +388,7 @@ bool workspace_switch(struct sway_container *workspace) {
387 free(prev_workspace_name); 388 free(prev_workspace_name);
388 prev_workspace_name = malloc(strlen(active_ws->name) + 1); 389 prev_workspace_name = malloc(strlen(active_ws->name) + 1);
389 if (!prev_workspace_name) { 390 if (!prev_workspace_name) {
390 wlr_log(L_ERROR, "Unable to allocate previous workspace name"); 391 wlr_log(WLR_ERROR, "Unable to allocate previous workspace name");
391 return false; 392 return false;
392 } 393 }
393 strcpy(prev_workspace_name, active_ws->name); 394 strcpy(prev_workspace_name, active_ws->name);
@@ -409,7 +410,7 @@ bool workspace_switch(struct sway_container *workspace) {
409 } 410 }
410 } 411 }
411 412
412 wlr_log(L_DEBUG, "Switching to workspace %p:%s", 413 wlr_log(WLR_DEBUG, "Switching to workspace %p:%s",
413 workspace, workspace->name); 414 workspace, workspace->name);
414 struct sway_container *next = seat_get_focus_inactive(seat, workspace); 415 struct sway_container *next = seat_get_focus_inactive(seat, workspace);
415 if (next == NULL) { 416 if (next == NULL) {
@@ -427,7 +428,7 @@ bool workspace_switch(struct sway_container *workspace) {
427 } 428 }
428 seat_set_focus(seat, next); 429 seat_set_focus(seat, next);
429 struct sway_container *output = container_parent(workspace, C_OUTPUT); 430 struct sway_container *output = container_parent(workspace, C_OUTPUT);
430 arrange_and_commit(output); 431 arrange_windows(output);
431 return true; 432 return true;
432} 433}
433 434
@@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available(
518 519
519 return NULL; 520 return NULL;
520} 521}
522
523void workspace_detect_urgent(struct sway_container *workspace) {
524 bool new_urgent = container_has_urgent_child(workspace);
525
526 if (workspace->sway_workspace->urgent != new_urgent) {
527 workspace->sway_workspace->urgent = new_urgent;
528 ipc_event_workspace(NULL, workspace, "urgent");
529 container_damage_whole(workspace);
530 }
531}
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 5b8028e5..94bc48bc 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -46,7 +46,7 @@ static void swaybar_output_free(struct swaybar_output *output) {
46 if (!output) { 46 if (!output) {
47 return; 47 return;
48 } 48 }
49 wlr_log(L_DEBUG, "Removing output %s", output->name); 49 wlr_log(WLR_DEBUG, "Removing output %s", output->name);
50 zwlr_layer_surface_v1_destroy(output->layer_surface); 50 zwlr_layer_surface_v1_destroy(output->layer_surface);
51 wl_surface_destroy(output->surface); 51 wl_surface_destroy(output->surface);
52 wl_output_destroy(output->output); 52 wl_output_destroy(output->output);
@@ -147,7 +147,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
147 && x < hotspot->x + hotspot->width 147 && x < hotspot->x + hotspot->width
148 && y < hotspot->y + hotspot->height) { 148 && y < hotspot->y + hotspot->height) {
149 hotspot->callback(output, pointer->x, pointer->y, 149 hotspot->callback(output, pointer->x, pointer->y,
150 button, hotspot->data); 150 wl_button_to_x11_button(button), hotspot->data);
151 } 151 }
152 } 152 }
153} 153}
@@ -155,11 +155,26 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
155static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, 155static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
156 uint32_t time, uint32_t axis, wl_fixed_t value) { 156 uint32_t time, uint32_t axis, wl_fixed_t value) {
157 struct swaybar *bar = data; 157 struct swaybar *bar = data;
158 struct swaybar_pointer *pointer = &bar->pointer;
158 struct swaybar_output *output = bar->pointer.current; 159 struct swaybar_output *output = bar->pointer.current;
159 if (!sway_assert(output, "axis with no active output")) { 160 if (!sway_assert(output, "axis with no active output")) {
160 return; 161 return;
161 } 162 }
162 163
164 struct swaybar_hotspot *hotspot;
165 wl_list_for_each(hotspot, &output->hotspots, link) {
166 double x = pointer->x * output->scale;
167 double y = pointer->y * output->scale;
168 if (x >= hotspot->x
169 && y >= hotspot->y
170 && x < hotspot->x + hotspot->width
171 && y < hotspot->y + hotspot->height) {
172 hotspot->callback(output, pointer->x, pointer->y,
173 wl_axis_to_x11_button(axis, value), hotspot->data);
174 return;
175 }
176 }
177
163 double amt = wl_fixed_to_double(value); 178 double amt = wl_fixed_to_double(value);
164 if (amt == 0.0) { 179 if (amt == 0.0) {
165 return; 180 return;
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 141612a6..78b183ad 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,5 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h> 2#include <json-c/json.h>
3#include <linux/input-event-codes.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <string.h> 5#include <string.h>
5#include <unistd.h> 6#include <unistd.h>
@@ -31,7 +32,7 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) {
31 status_error(status, "[failed to parse i3bar json]"); 32 status_error(status, "[failed to parse i3bar json]");
32 return false; 33 return false;
33 } 34 }
34 wlr_log(L_DEBUG, "Got i3bar json: '%s'", text); 35 wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text);
35 for (size_t i = 0; i < json_object_array_length(results); ++i) { 36 for (size_t i = 0; i < json_object_array_length(results); ++i) {
36 json_object *full_text, *short_text, *color, *min_width, *align, *urgent; 37 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
37 json_object *name, *instance, *separator, *separator_block_width; 38 json_object *name, *instance, *separator, *separator_block_width;
@@ -192,8 +193,8 @@ bool i3bar_handle_readable(struct status_line *status) {
192} 193}
193 194
194void i3bar_block_send_click(struct status_line *status, 195void i3bar_block_send_click(struct status_line *status,
195 struct i3bar_block *block, int x, int y, uint32_t button) { 196 struct i3bar_block *block, int x, int y, enum x11_button button) {
196 wlr_log(L_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); 197 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
197 if (!block->name || !status->i3bar_state.click_events) { 198 if (!block->name || !status->i3bar_state.click_events) {
198 return; 199 return;
199 } 200 }
@@ -215,3 +216,32 @@ void i3bar_block_send_click(struct status_line *status,
215 } 216 }
216 json_object_put(event_json); 217 json_object_put(event_json);
217} 218}
219
220enum x11_button wl_button_to_x11_button(uint32_t button) {
221 switch (button) {
222 case BTN_LEFT:
223 return LEFT;
224 case BTN_MIDDLE:
225 return MIDDLE;
226 case BTN_RIGHT:
227 return RIGHT;
228 case BTN_SIDE:
229 return BACK;
230 case BTN_EXTRA:
231 return FORWARD;
232 default:
233 return NONE;
234 }
235}
236
237enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) {
238 switch (axis) {
239 case WL_POINTER_AXIS_VERTICAL_SCROLL:
240 return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN;
241 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
242 return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT;
243 default:
244 wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll");
245 return NONE;
246 }
247}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 959fa095..c2d05920 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -115,6 +115,18 @@ static void ipc_parse_colors(
115 config->colors.inactive_workspace.text = parse_color( 115 config->colors.inactive_workspace.text = parse_color(
116 json_object_get_string(inactive_workspace_text)); 116 json_object_get_string(inactive_workspace_text));
117 } 117 }
118 if (urgent_workspace_border) {
119 config->colors.urgent_workspace.border = parse_color(
120 json_object_get_string(urgent_workspace_border));
121 }
122 if (urgent_workspace_bg) {
123 config->colors.urgent_workspace.background = parse_color(
124 json_object_get_string(urgent_workspace_bg));
125 }
126 if (urgent_workspace_text) {
127 config->colors.urgent_workspace.text = parse_color(
128 json_object_get_string(urgent_workspace_text));
129 }
118 if (binding_mode_border) { 130 if (binding_mode_border) {
119 config->colors.binding_mode.border = parse_color( 131 config->colors.binding_mode.border = parse_color(
120 json_object_get_string(binding_mode_border)); 132 json_object_get_string(binding_mode_border));
@@ -327,7 +339,7 @@ bool handle_ipc_readable(struct swaybar *bar) {
327 json_object *result = json_tokener_parse(resp->payload); 339 json_object *result = json_tokener_parse(resp->payload);
328 if (!result) { 340 if (!result) {
329 free_ipc_response(resp); 341 free_ipc_response(resp);
330 wlr_log(L_ERROR, "failed to parse payload as json"); 342 wlr_log(WLR_ERROR, "failed to parse payload as json");
331 return false; 343 return false;
332 } 344 }
333 json_object *json_change, *json_pango_markup; 345 json_object *json_change, *json_pango_markup;
@@ -340,7 +352,7 @@ bool handle_ipc_readable(struct swaybar *bar) {
340 bar->config->mode = strdup(change); 352 bar->config->mode = strdup(change);
341 } 353 }
342 } else { 354 } else {
343 wlr_log(L_ERROR, "failed to parse response"); 355 wlr_log(WLR_ERROR, "failed to parse response");
344 json_object_put(result); 356 json_object_put(result);
345 free_ipc_response(resp); 357 free_ipc_response(resp);
346 return false; 358 return false;
diff --git a/swaybar/main.c b/swaybar/main.c
index c897e1c9..60e4b37c 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -75,13 +75,13 @@ int main(int argc, char **argv) {
75 } 75 }
76 76
77 if (debug) { 77 if (debug) {
78 wlr_log_init(L_DEBUG, NULL); 78 wlr_log_init(WLR_DEBUG, NULL);
79 } else { 79 } else {
80 wlr_log_init(L_ERROR, NULL); 80 wlr_log_init(WLR_ERROR, NULL);
81 } 81 }
82 82
83 if (!bar_id) { 83 if (!bar_id) {
84 wlr_log(L_ERROR, "No bar_id passed. " 84 wlr_log(WLR_ERROR, "No bar_id passed. "
85 "Provide --bar_id or let sway start swaybar"); 85 "Provide --bar_id or let sway start swaybar");
86 return 1; 86 return 1;
87 } 87 }
@@ -89,7 +89,7 @@ int main(int argc, char **argv) {
89 if (!socket_path) { 89 if (!socket_path) {
90 socket_path = get_socketpath(); 90 socket_path = get_socketpath();
91 if (!socket_path) { 91 if (!socket_path) {
92 wlr_log(L_ERROR, "Unable to retrieve socket path"); 92 wlr_log(WLR_ERROR, "Unable to retrieve socket path");
93 return 1; 93 return 1;
94 } 94 }
95 } 95 }
diff --git a/swaybar/render.c b/swaybar/render.c
index 2ebd338e..d210e25a 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -109,7 +109,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109} 109}
110 110
111static void block_hotspot_callback(struct swaybar_output *output, 111static void block_hotspot_callback(struct swaybar_output *output,
112 int x, int y, uint32_t button, void *data) { 112 int x, int y, enum x11_button button, void *data) {
113 struct i3bar_block *block = data; 113 struct i3bar_block *block = data;
114 struct status_line *status = output->bar->status; 114 struct status_line *status = output->bar->status;
115 i3bar_block_send_click(status, block, x, y, button); 115 i3bar_block_send_click(status, block, x, y, button);
@@ -349,7 +349,7 @@ static const char *strip_workspace_number(const char *ws_name) {
349} 349}
350 350
351static void workspace_hotspot_callback(struct swaybar_output *output, 351static void workspace_hotspot_callback(struct swaybar_output *output,
352 int x, int y, uint32_t button, void *data) { 352 int x, int y, enum x11_button button, void *data) {
353 ipc_send_workspace_command(output->bar, (const char *)data); 353 ipc_send_workspace_command(output->bar, (const char *)data);
354} 354}
355 355
@@ -503,6 +503,9 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
503 output->buffers, 503 output->buffers,
504 output->width * output->scale, 504 output->width * output->scale,
505 output->height * output->scale); 505 output->height * output->scale);
506 if (!output->current_buffer) {
507 return;
508 }
506 cairo_t *shm = output->current_buffer->cairo; 509 cairo_t *shm = output->current_buffer->cairo;
507 510
508 cairo_save(shm); 511 cairo_save(shm);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index e0e7414a..bc47580b 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -49,14 +49,14 @@ bool status_handle_readable(struct status_line *status) {
49 json_object *version; 49 json_object *version;
50 if (json_object_object_get_ex(proto, "version", &version) 50 if (json_object_object_get_ex(proto, "version", &version)
51 && json_object_get_int(version) == 1) { 51 && json_object_get_int(version) == 1) {
52 wlr_log(L_DEBUG, "Switched to i3bar protocol."); 52 wlr_log(WLR_DEBUG, "Switched to i3bar protocol.");
53 status->protocol = PROTOCOL_I3BAR; 53 status->protocol = PROTOCOL_I3BAR;
54 } 54 }
55 json_object *click_events; 55 json_object *click_events;
56 if (json_object_object_get_ex( 56 if (json_object_object_get_ex(
57 proto, "click_events", &click_events) 57 proto, "click_events", &click_events)
58 && json_object_get_boolean(click_events)) { 58 && json_object_get_boolean(click_events)) {
59 wlr_log(L_DEBUG, "Enabled click events."); 59 wlr_log(WLR_DEBUG, "Enabled click events.");
60 status->i3bar_state.click_events = true; 60 status->i3bar_state.click_events = true;
61 const char *events_array = "[\n"; 61 const char *events_array = "[\n";
62 ssize_t len = strlen(events_array); 62 ssize_t len = strlen(events_array);
@@ -91,7 +91,7 @@ struct status_line *status_line_init(char *cmd) {
91 int pipe_read_fd[2]; 91 int pipe_read_fd[2];
92 int pipe_write_fd[2]; 92 int pipe_write_fd[2];
93 if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { 93 if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) {
94 wlr_log(L_ERROR, "Unable to create pipes for status_command fork"); 94 wlr_log(WLR_ERROR, "Unable to create pipes for status_command fork");
95 exit(1); 95 exit(1);
96 } 96 }
97 97
diff --git a/swaybg/main.c b/swaybg/main.c
index 5b6c378c..f8e7e7ef 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -48,7 +48,7 @@ struct swaybg_state {
48bool is_valid_color(const char *color) { 48bool is_valid_color(const char *color) {
49 int len = strlen(color); 49 int len = strlen(color);
50 if (len != 7 || color[0] != '#') { 50 if (len != 7 || color[0] != '#') {
51 wlr_log(L_ERROR, "%s is not a valid color for swaybg. " 51 wlr_log(WLR_ERROR, "%s is not a valid color for swaybg. "
52 "Color should be specified as #rrggbb (no alpha).", color); 52 "Color should be specified as #rrggbb (no alpha).", color);
53 return false; 53 return false;
54 } 54 }
@@ -68,6 +68,9 @@ static void render_frame(struct swaybg_state *state) {
68 buffer_height = state->height * state->scale; 68 buffer_height = state->height * state->scale;
69 state->current_buffer = get_next_buffer(state->shm, 69 state->current_buffer = get_next_buffer(state->shm,
70 state->buffers, buffer_width, buffer_height); 70 state->buffers, buffer_width, buffer_height);
71 if (!state->current_buffer) {
72 return;
73 }
71 cairo_t *cairo = state->current_buffer->cairo; 74 cairo_t *cairo = state->current_buffer->cairo;
72 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { 75 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
73 cairo_set_source_u32(cairo, state->context.color); 76 cairo_set_source_u32(cairo, state->context.color);
@@ -185,10 +188,10 @@ int main(int argc, const char **argv) {
185 struct swaybg_args args = {0}; 188 struct swaybg_args args = {0};
186 struct swaybg_state state = {0}; 189 struct swaybg_state state = {0};
187 state.args = &args; 190 state.args = &args;
188 wlr_log_init(L_DEBUG, NULL); 191 wlr_log_init(WLR_DEBUG, NULL);
189 192
190 if (argc != 4) { 193 if (argc != 4) {
191 wlr_log(L_ERROR, "Do not run this program manually. " 194 wlr_log(WLR_ERROR, "Do not run this program manually. "
192 "See man 5 sway and look for output options."); 195 "See man 5 sway and look for output options.");
193 return 1; 196 return 1;
194 } 197 }
diff --git a/swayidle/main.c b/swayidle/main.c
index 7666578f..678d622f 100644
--- a/swayidle/main.c
+++ b/swayidle/main.c
@@ -1,22 +1,21 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <errno.h>
2#include <getopt.h> 3#include <getopt.h>
3#include <signal.h>
4#include <pthread.h> 4#include <pthread.h>
5#include <signal.h>
5#include <stdio.h> 6#include <stdio.h>
6#include <stdlib.h> 7#include <stdlib.h>
7#include <errno.h>
8#include <string.h> 8#include <string.h>
9#include <sys/wait.h> 9#include <sys/wait.h>
10#include <unistd.h> 10#include <unistd.h>
11#include <wayland-client-protocol.h> 11#include <wayland-client-protocol.h>
12#include <wayland-client.h> 12#include <wayland-client.h>
13#include <wayland-server.h>
13#include <wayland-util.h> 14#include <wayland-util.h>
14#include <wlr/config.h> 15#include <wlr/config.h>
15#include <wlr/util/log.h> 16#include <wlr/util/log.h>
16#include <wlr/types/wlr_output_layout.h>
17#include <wlr/types/wlr_output.h>
18#include "idle-client-protocol.h"
19#include "config.h" 17#include "config.h"
18#include "idle-client-protocol.h"
20#include "list.h" 19#include "list.h"
21#ifdef SWAY_IDLE_HAS_SYSTEMD 20#ifdef SWAY_IDLE_HAS_SYSTEMD
22#include <systemd/sd-bus.h> 21#include <systemd/sd-bus.h>
@@ -36,7 +35,6 @@ struct swayidle_state {
36 struct wl_display *display; 35 struct wl_display *display;
37 struct org_kde_kwin_idle_timeout *idle_timer; 36 struct org_kde_kwin_idle_timeout *idle_timer;
38 struct org_kde_kwin_idle_timeout *lock_timer; 37 struct org_kde_kwin_idle_timeout *lock_timer;
39 struct wlr_output_layout *layout;
40 struct wl_event_loop *event_loop; 38 struct wl_event_loop *event_loop;
41 list_t *timeout_cmds; 39 list_t *timeout_cmds;
42} state; 40} state;
@@ -59,24 +57,24 @@ static void cmd_exec(void *data) {
59 return; 57 return;
60 } 58 }
61 char *param = (char *)data; 59 char *param = (char *)data;
62 wlr_log(L_DEBUG, "Cmd exec %s", param); 60 wlr_log(WLR_DEBUG, "Cmd exec %s", param);
63 pid_t pid = fork(); 61 pid_t pid = fork();
64 if (pid == 0) { 62 if (pid == 0) {
65 pid = fork(); 63 pid = fork();
66 if (pid == 0) { 64 if (pid == 0) {
67 char *const cmd[] = { "sh", "-c", param, NULL, }; 65 char *const cmd[] = { "sh", "-c", param, NULL, };
68 execvp(cmd[0], cmd); 66 execvp(cmd[0], cmd);
69 wlr_log_errno(L_ERROR, "execve failed!"); 67 wlr_log_errno(WLR_ERROR, "execve failed!");
70 exit(1); 68 exit(1);
71 } else if (pid < 0) { 69 } else if (pid < 0) {
72 wlr_log_errno(L_ERROR, "fork failed"); 70 wlr_log_errno(WLR_ERROR, "fork failed");
73 exit(1); 71 exit(1);
74 } 72 }
75 exit(0); 73 exit(0);
76 } else if (pid < 0) { 74 } else if (pid < 0) {
77 wlr_log_errno(L_ERROR, "fork failed"); 75 wlr_log_errno(WLR_ERROR, "fork failed");
78 } else { 76 } else {
79 wlr_log(L_DEBUG, "Spawned process %s", param); 77 wlr_log(WLR_DEBUG, "Spawned process %s", param);
80 waitpid(pid, NULL, 0); 78 waitpid(pid, NULL, 0);
81 } 79 }
82} 80}
@@ -86,7 +84,7 @@ static int lock_fd = -1;
86static int ongoing_fd = -1; 84static int ongoing_fd = -1;
87 85
88static int release_lock(void *data) { 86static int release_lock(void *data) {
89 wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd); 87 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
90 if (ongoing_fd >= 0) { 88 if (ongoing_fd >= 0) {
91 close(ongoing_fd); 89 close(ongoing_fd);
92 } 90 }
@@ -101,7 +99,7 @@ void acquire_sleep_lock() {
101 int ret = sd_bus_default_system(&bus); 99 int ret = sd_bus_default_system(&bus);
102 100
103 if (ret < 0) { 101 if (ret < 0) {
104 wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", 102 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
105 strerror(-ret)); 103 strerror(-ret));
106 return; 104 return;
107 } 105 }
@@ -112,17 +110,17 @@ void acquire_sleep_lock() {
112 &error, &msg, "ssss", "sleep", "swayidle", 110 &error, &msg, "ssss", "sleep", "swayidle",
113 "Setup Up Lock Screen", "delay"); 111 "Setup Up Lock Screen", "delay");
114 if (ret < 0) { 112 if (ret < 0) {
115 wlr_log(L_ERROR, "Failed to send Inhibit signal: %s", 113 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s",
116 strerror(-ret)); 114 strerror(-ret));
117 } else { 115 } else {
118 ret = sd_bus_message_read(msg, "h", &lock_fd); 116 ret = sd_bus_message_read(msg, "h", &lock_fd);
119 if (ret < 0) { 117 if (ret < 0) {
120 wlr_log(L_ERROR, 118 wlr_log(WLR_ERROR,
121 "Failed to parse D-Bus response for Inhibit: %s", 119 "Failed to parse D-Bus response for Inhibit: %s",
122 strerror(-ret)); 120 strerror(-ret));
123 } 121 }
124 } 122 }
125 wlr_log(L_INFO, "Got sleep lock: %d", lock_fd); 123 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
126} 124}
127 125
128static int prepare_for_sleep(sd_bus_message *msg, void *userdata, 126static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
@@ -131,10 +129,10 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
131 int going_down = 1; 129 int going_down = 1;
132 int ret = sd_bus_message_read(msg, "b", &going_down); 130 int ret = sd_bus_message_read(msg, "b", &going_down);
133 if (ret < 0) { 131 if (ret < 0) {
134 wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s", 132 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
135 strerror(-ret)); 133 strerror(-ret));
136 } 134 }
137 wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down); 135 wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);
138 if (!going_down) { 136 if (!going_down) {
139 acquire_sleep_lock(); 137 acquire_sleep_lock();
140 return 0; 138 return 0;
@@ -151,7 +149,7 @@ static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
151 wl_event_loop_add_timer(state.event_loop, release_lock, NULL); 149 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
152 wl_event_source_timer_update(source, 1000); 150 wl_event_source_timer_update(source, 1000);
153 } 151 }
154 wlr_log(L_DEBUG, "Prepare for sleep done"); 152 wlr_log(WLR_DEBUG, "Prepare for sleep done");
155 return 0; 153 return 0;
156} 154}
157 155
@@ -165,10 +163,10 @@ static int dbus_event(int fd, uint32_t mask, void *data) {
165 163
166void setup_sleep_listener() { 164void setup_sleep_listener() {
167 struct sd_bus *bus; 165 struct sd_bus *bus;
168 166
169 int ret = sd_bus_default_system(&bus); 167 int ret = sd_bus_default_system(&bus);
170 if (ret < 0) { 168 if (ret < 0) {
171 wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", 169 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
172 strerror(-ret)); 170 strerror(-ret));
173 return; 171 return;
174 } 172 }
@@ -183,7 +181,7 @@ void setup_sleep_listener() {
183 "/org/freedesktop/login1"); 181 "/org/freedesktop/login1");
184 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL); 182 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
185 if (ret < 0) { 183 if (ret < 0) {
186 wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); 184 wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
187 return; 185 return;
188 } 186 }
189 acquire_sleep_lock(); 187 acquire_sleep_lock();
@@ -214,7 +212,7 @@ static const struct wl_registry_listener registry_listener = {
214 212
215static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) { 213static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
216 struct swayidle_timeout_cmd *cmd = data; 214 struct swayidle_timeout_cmd *cmd = data;
217 wlr_log(L_DEBUG, "idle state"); 215 wlr_log(WLR_DEBUG, "idle state");
218 if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) { 216 if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) {
219 cmd->idle_cmd->callback(cmd->idle_cmd->param); 217 cmd->idle_cmd->callback(cmd->idle_cmd->param);
220 } 218 }
@@ -222,7 +220,7 @@ static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
222 220
223static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) { 221static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
224 struct swayidle_timeout_cmd *cmd = data; 222 struct swayidle_timeout_cmd *cmd = data;
225 wlr_log(L_DEBUG, "active state"); 223 wlr_log(WLR_DEBUG, "active state");
226 if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) { 224 if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) {
227 cmd->resume_cmd->callback(cmd->resume_cmd->param); 225 cmd->resume_cmd->callback(cmd->resume_cmd->param);
228 } 226 }
@@ -235,12 +233,12 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
235 233
236struct swayidle_cmd *parse_command(int argc, char **argv) { 234struct swayidle_cmd *parse_command(int argc, char **argv) {
237 if (argc < 1) { 235 if (argc < 1) {
238 wlr_log(L_ERROR, "Too few parameters for command in parse_command"); 236 wlr_log(WLR_ERROR, "Too few parameters for command in parse_command");
239 return NULL; 237 return NULL;
240 } 238 }
241 239
242 struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd)); 240 struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd));
243 wlr_log(L_DEBUG, "Command: %s", argv[0]); 241 wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
244 cmd->callback = cmd_exec; 242 cmd->callback = cmd_exec;
245 cmd->param = argv[0]; 243 cmd->param = argv[0];
246 return cmd; 244 return cmd;
@@ -248,7 +246,7 @@ struct swayidle_cmd *parse_command(int argc, char **argv) {
248 246
249int parse_timeout(int argc, char **argv) { 247int parse_timeout(int argc, char **argv) {
250 if (argc < 3) { 248 if (argc < 3) {
251 wlr_log(L_ERROR, "Too few parameters to timeout command. " 249 wlr_log(WLR_ERROR, "Too few parameters to timeout command. "
252 "Usage: timeout <seconds> <command>"); 250 "Usage: timeout <seconds> <command>");
253 exit(-1); 251 exit(-1);
254 } 252 }
@@ -256,7 +254,7 @@ int parse_timeout(int argc, char **argv) {
256 char *endptr; 254 char *endptr;
257 int seconds = strtoul(argv[1], &endptr, 10); 255 int seconds = strtoul(argv[1], &endptr, 10);
258 if (errno != 0 || *endptr != '\0') { 256 if (errno != 0 || *endptr != '\0') {
259 wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a " 257 wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a "
260 "numeric value representing seconds", optarg); 258 "numeric value representing seconds", optarg);
261 exit(-1); 259 exit(-1);
262 } 260 }
@@ -264,13 +262,13 @@ int parse_timeout(int argc, char **argv) {
264 calloc(1, sizeof(struct swayidle_timeout_cmd)); 262 calloc(1, sizeof(struct swayidle_timeout_cmd));
265 cmd->timeout = seconds * 1000; 263 cmd->timeout = seconds * 1000;
266 264
267 wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout); 265 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
268 wlr_log(L_DEBUG, "Setup idle"); 266 wlr_log(WLR_DEBUG, "Setup idle");
269 cmd->idle_cmd = parse_command(argc - 2, &argv[2]); 267 cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
270 268
271 int result = 3; 269 int result = 3;
272 if (argc >= 5 && !strcmp("resume", argv[3])) { 270 if (argc >= 5 && !strcmp("resume", argv[3])) {
273 wlr_log(L_DEBUG, "Setup resume"); 271 wlr_log(WLR_DEBUG, "Setup resume");
274 cmd->resume_cmd = parse_command(argc - 4, &argv[4]); 272 cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
275 result = 5; 273 result = 5;
276 } 274 }
@@ -280,14 +278,14 @@ int parse_timeout(int argc, char **argv) {
280 278
281int parse_sleep(int argc, char **argv) { 279int parse_sleep(int argc, char **argv) {
282 if (argc < 2) { 280 if (argc < 2) {
283 wlr_log(L_ERROR, "Too few parameters to before-sleep command. " 281 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
284 "Usage: before-sleep <command>"); 282 "Usage: before-sleep <command>");
285 exit(-1); 283 exit(-1);
286 } 284 }
287 285
288 lock_cmd = parse_command(argc - 1, &argv[1]); 286 lock_cmd = parse_command(argc - 1, &argv[1]);
289 if (lock_cmd) { 287 if (lock_cmd) {
290 wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param); 288 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", lock_cmd->param);
291 } 289 }
292 290
293 return 2; 291 return 2;
@@ -314,10 +312,10 @@ int parse_args(int argc, char *argv[]) {
314 } 312 }
315 313
316 if (debug) { 314 if (debug) {
317 wlr_log_init(L_DEBUG, NULL); 315 wlr_log_init(WLR_DEBUG, NULL);
318 wlr_log(L_DEBUG, "Loglevel debug"); 316 wlr_log(WLR_DEBUG, "Loglevel debug");
319 } else { 317 } else {
320 wlr_log_init(L_INFO, NULL); 318 wlr_log_init(WLR_INFO, NULL);
321 } 319 }
322 320
323 321
@@ -326,13 +324,13 @@ int parse_args(int argc, char *argv[]) {
326 int i = optind; 324 int i = optind;
327 while (i < argc) { 325 while (i < argc) {
328 if (!strcmp("timeout", argv[i])) { 326 if (!strcmp("timeout", argv[i])) {
329 wlr_log(L_DEBUG, "Got timeout"); 327 wlr_log(WLR_DEBUG, "Got timeout");
330 i += parse_timeout(argc - i, &argv[i]); 328 i += parse_timeout(argc - i, &argv[i]);
331 } else if (!strcmp("before-sleep", argv[i])) { 329 } else if (!strcmp("before-sleep", argv[i])) {
332 wlr_log(L_DEBUG, "Got before-sleep"); 330 wlr_log(WLR_DEBUG, "Got before-sleep");
333 i += parse_sleep(argc - i, &argv[i]); 331 i += parse_sleep(argc - i, &argv[i]);
334 } else { 332 } else {
335 wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]); 333 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
336 exit(-1); 334 exit(-1);
337 } 335 }
338 } 336 }
@@ -358,16 +356,16 @@ static int display_event(int fd, uint32_t mask, void *data) {
358 sway_terminate(0); 356 sway_terminate(0);
359 } 357 }
360 if (wl_display_dispatch(state.display) < 0) { 358 if (wl_display_dispatch(state.display) < 0) {
361 wlr_log_errno(L_ERROR, "wl_display_dispatch failed, exiting"); 359 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
362 sway_terminate(0); 360 sway_terminate(0);
363 }; 361 }
364 return 0; 362 return 0;
365} 363}
366 364
367void register_idle_timeout(void *item) { 365void register_idle_timeout(void *item) {
368 struct swayidle_timeout_cmd *cmd = item; 366 struct swayidle_timeout_cmd *cmd = item;
369 if (cmd == NULL || !cmd->timeout) { 367 if (cmd == NULL || !cmd->timeout) {
370 wlr_log(L_ERROR, "Invalid idle cmd, will not register"); 368 wlr_log(WLR_ERROR, "Invalid idle cmd, will not register");
371 return; 369 return;
372 } 370 }
373 state.idle_timer = 371 state.idle_timer =
@@ -376,7 +374,7 @@ void register_idle_timeout(void *item) {
376 org_kde_kwin_idle_timeout_add_listener(state.idle_timer, 374 org_kde_kwin_idle_timeout_add_listener(state.idle_timer,
377 &idle_timer_listener, cmd); 375 &idle_timer_listener, cmd);
378 } else { 376 } else {
379 wlr_log(L_ERROR, "Could not create idle timer"); 377 wlr_log(WLR_ERROR, "Could not create idle timer");
380 } 378 }
381} 379}
382 380
@@ -390,22 +388,21 @@ int main(int argc, char *argv[]) {
390 388
391 state.display = wl_display_connect(NULL); 389 state.display = wl_display_connect(NULL);
392 if (state.display == NULL) { 390 if (state.display == NULL) {
393 wlr_log(L_ERROR, "Failed to create display"); 391 wlr_log(WLR_ERROR, "Failed to create display");
394 return -3; 392 return -3;
395 } 393 }
396 394
397 struct wl_registry *registry = wl_display_get_registry(state.display); 395 struct wl_registry *registry = wl_display_get_registry(state.display);
398 wl_registry_add_listener(registry, &registry_listener, NULL); 396 wl_registry_add_listener(registry, &registry_listener, NULL);
399 wl_display_roundtrip(state.display); 397 wl_display_roundtrip(state.display);
400 state.layout = wlr_output_layout_create();
401 state.event_loop = wl_event_loop_create(); 398 state.event_loop = wl_event_loop_create();
402 399
403 if (idle_manager == NULL) { 400 if (idle_manager == NULL) {
404 wlr_log(L_ERROR, "Display doesn't support idle protocol"); 401 wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
405 return -4; 402 return -4;
406 } 403 }
407 if (seat == NULL) { 404 if (seat == NULL) {
408 wlr_log(L_ERROR, "Seat error"); 405 wlr_log(WLR_ERROR, "Seat error");
409 return -5; 406 return -5;
410 } 407 }
411 408
@@ -417,7 +414,7 @@ int main(int argc, char *argv[]) {
417 } 414 }
418#endif 415#endif
419 if (!should_run) { 416 if (!should_run) {
420 wlr_log(L_INFO, "No command specified! Nothing to do, will exit"); 417 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
421 sway_terminate(0); 418 sway_terminate(0);
422 } 419 }
423 list_foreach(state.timeout_cmds, register_idle_timeout); 420 list_foreach(state.timeout_cmds, register_idle_timeout);
diff --git a/swaylock/main.c b/swaylock/main.c
index 73c2b5d6..ae5b86b9 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -21,6 +21,7 @@
21#include "pool-buffer.h" 21#include "pool-buffer.h"
22#include "cairo.h" 22#include "cairo.h"
23#include "log.h" 23#include "log.h"
24#include "readline.h"
24#include "stringop.h" 25#include "stringop.h"
25#include "util.h" 26#include "util.h"
26#include "wlr-input-inhibitor-unstable-v1-client-protocol.h" 27#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
@@ -34,7 +35,7 @@ void sway_terminate(int exit_code) {
34static void daemonize() { 35static void daemonize() {
35 int fds[2]; 36 int fds[2];
36 if (pipe(fds) != 0) { 37 if (pipe(fds) != 0) {
37 wlr_log(L_ERROR, "Failed to pipe"); 38 wlr_log(WLR_ERROR, "Failed to pipe");
38 exit(1); 39 exit(1);
39 } 40 }
40 if (fork() == 0) { 41 if (fork() == 0) {
@@ -58,7 +59,7 @@ static void daemonize() {
58 close(fds[1]); 59 close(fds[1]);
59 uint8_t success; 60 uint8_t success;
60 if (read(fds[0], &success, 1) != 1 || !success) { 61 if (read(fds[0], &success, 1) != 1 || !success) {
61 wlr_log(L_ERROR, "Failed to daemonize"); 62 wlr_log(WLR_ERROR, "Failed to daemonize");
62 exit(1); 63 exit(1);
63 } 64 }
64 close(fds[0]); 65 close(fds[0]);
@@ -89,7 +90,7 @@ static bool surface_is_opaque(struct swaylock_surface *surface) {
89 if (surface->image) { 90 if (surface->image) {
90 return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR; 91 return cairo_surface_get_content(surface->image) == CAIRO_CONTENT_COLOR;
91 } 92 }
92 return (surface->state->args.color & 0xff) == 0xff; 93 return (surface->state->args.colors.background & 0xff) == 0xff;
93} 94}
94 95
95static void create_layer_surface(struct swaylock_surface *surface) { 96static void create_layer_surface(struct swaylock_surface *surface) {
@@ -238,7 +239,7 @@ static void handle_xdg_output_logical_position(void *data,
238 239
239static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, 240static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output,
240 const char *name) { 241 const char *name) {
241 wlr_log(L_DEBUG, "output name is %s", name); 242 wlr_log(WLR_DEBUG, "output name is %s", name);
242 struct swaylock_surface *surface = data; 243 struct swaylock_surface *surface = data;
243 surface->xdg_output = output; 244 surface->xdg_output = output;
244 surface->output_name = strdup(name); 245 surface->output_name = strdup(name);
@@ -354,10 +355,10 @@ static void load_image(char *arg, struct swaylock_state *state) {
354 } 355 }
355 if (exists) { 356 if (exists) {
356 if (image->output_name) { 357 if (image->output_name) {
357 wlr_log(L_ERROR, "Multiple images defined for output %s", 358 wlr_log(WLR_ERROR, "Multiple images defined for output %s",
358 image->output_name); 359 image->output_name);
359 } else { 360 } else {
360 wlr_log(L_ERROR, "Multiple default images defined"); 361 wlr_log(WLR_ERROR, "Multiple default images defined");
361 } 362 }
362 } 363 }
363 364
@@ -377,71 +378,232 @@ static void load_image(char *arg, struct swaylock_state *state) {
377 } 378 }
378 wl_list_insert(&state->images, &image->link); 379 wl_list_insert(&state->images, &image->link);
379 state->args.mode = BACKGROUND_MODE_FILL; 380 state->args.mode = BACKGROUND_MODE_FILL;
380 wlr_log(L_DEBUG, "Loaded image %s for output %s", 381 wlr_log(WLR_DEBUG, "Loaded image %s for output %s",
381 image->path, image->output_name ? image->output_name : "*"); 382 image->path, image->output_name ? image->output_name : "*");
382} 383}
383 384
384static struct swaylock_state state; 385static void set_default_colors(struct swaylock_colors *colors) {
386 colors->background = 0xFFFFFFFF;
387 colors->bs_highlight = 0xDB3300FF;
388 colors->key_highlight = 0x33DB00FF;
389 colors->separator = 0x000000FF;
390 colors->inside = (struct swaylock_colorset){
391 .input = 0x000000C0,
392 .cleared = 0xE5A445C0,
393 .verifying = 0x0072FFC0,
394 .wrong = 0xFA0000C0,
395 };
396 colors->line = (struct swaylock_colorset){
397 .input = 0x000000FF,
398 .cleared = 0x000000FF,
399 .verifying = 0x000000FF,
400 .wrong = 0x000000FF,
401 };
402 colors->ring = (struct swaylock_colorset){
403 .input = 0x337D00FF,
404 .cleared = 0xE5A445FF,
405 .verifying = 0x3300FFFF,
406 .wrong = 0x7D3300FF,
407 };
408 colors->text = (struct swaylock_colorset){
409 .input = 0xE5A445FF,
410 .cleared = 0x000000FF,
411 .verifying = 0x000000FF,
412 .wrong = 0x000000FF,
413 };
414}
415
416enum line_mode {
417 LM_LINE,
418 LM_INSIDE,
419 LM_RING,
420};
421
422static int parse_options(int argc, char **argv, struct swaylock_state *state,
423 enum line_mode *line_mode) {
424 enum long_option_codes {
425 LO_BS_HL_COLOR = 256,
426 LO_FONT,
427 LO_IND_RADIUS,
428 LO_IND_THICKNESS,
429 LO_INSIDE_COLOR,
430 LO_INSIDE_CLEAR_COLOR,
431 LO_INSIDE_VER_COLOR,
432 LO_INSIDE_WRONG_COLOR,
433 LO_KEY_HL_COLOR,
434 LO_LINE_COLOR,
435 LO_LINE_CLEAR_COLOR,
436 LO_LINE_VER_COLOR,
437 LO_LINE_WRONG_COLOR,
438 LO_RING_COLOR,
439 LO_RING_CLEAR_COLOR,
440 LO_RING_VER_COLOR,
441 LO_RING_WRONG_COLOR,
442 LO_SEP_COLOR,
443 LO_TEXT_COLOR,
444 LO_TEXT_CLEAR_COLOR,
445 LO_TEXT_VER_COLOR,
446 LO_TEXT_WRONG_COLOR,
447 };
385 448
386int main(int argc, char **argv) {
387 static struct option long_options[] = { 449 static struct option long_options[] = {
388 {"help", no_argument, NULL, 'h'}, 450 {"config", required_argument, NULL, 'C'},
389 {"color", required_argument, NULL, 'c'}, 451 {"color", required_argument, NULL, 'c'},
452 {"ignore-empty-password", no_argument, NULL, 'e'},
453 {"daemonize", no_argument, NULL, 'f'},
454 {"help", no_argument, NULL, 'h'},
390 {"image", required_argument, NULL, 'i'}, 455 {"image", required_argument, NULL, 'i'},
456 {"line-uses-inside", no_argument, NULL, 'n'},
457 {"socket", required_argument, NULL, 'p'},
458 {"line-uses-ring", no_argument, NULL, 'r'},
391 {"scaling", required_argument, NULL, 's'}, 459 {"scaling", required_argument, NULL, 's'},
392 {"tiling", no_argument, NULL, 't'}, 460 {"tiling", no_argument, NULL, 't'},
393 {"version", no_argument, NULL, 'v'},
394 {"socket", required_argument, NULL, 'p'},
395 {"no-unlock-indicator", no_argument, NULL, 'u'}, 461 {"no-unlock-indicator", no_argument, NULL, 'u'},
396 {"daemonize", no_argument, NULL, 'f'}, 462 {"version", no_argument, NULL, 'v'},
463 {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR},
464 {"font", required_argument, NULL, LO_FONT},
465 {"indicator-radius", required_argument, NULL, LO_IND_RADIUS},
466 {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS},
467 {"inside-color", required_argument, NULL, LO_INSIDE_COLOR},
468 {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR},
469 {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR},
470 {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR},
471 {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR},
472 {"line-color", required_argument, NULL, LO_LINE_COLOR},
473 {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR},
474 {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR},
475 {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR},
476 {"ring-color", required_argument, NULL, LO_RING_COLOR},
477 {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR},
478 {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR},
479 {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR},
480 {"separator-color", required_argument, NULL, LO_SEP_COLOR},
481 {"text-color", required_argument, NULL, LO_TEXT_COLOR},
482 {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR},
483 {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR},
484 {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR},
397 {0, 0, 0, 0} 485 {0, 0, 0, 0}
398 }; 486 };
399 487
400 const char usage[] = 488 const char usage[] =
401 "Usage: swaylock [options...]\n" 489 "Usage: swaylock [options...]\n"
402 "\n" 490 "\n"
403 " -h, --help Show help message and quit.\n" 491 " -C, --config <config_file> "
404 " -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n" 492 "Path to the config file.\n"
405 " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" 493 " -c, --color <color> "
406 " -t, --tiling Same as --scaling=tile.\n" 494 "Turn the screen into the given color instead of white.\n"
407 " -v, --version Show the version number and quit.\n" 495 " -e, --ignore-empty-password "
408 " -i, --image [<output>:]<path> Display the given image.\n" 496 "When an empty password is provided, do not validate it.\n"
409 " -u, --no-unlock-indicator Disable the unlock indicator.\n" 497 " -f, --daemonize "
410 " -f, --daemonize Detach from the controlling terminal after locking.\n"; 498 "Detach from the controlling terminal after locking.\n"
411 499 " -h, --help "
412 state.args = (struct swaylock_args){ 500 "Show help message and quit.\n"
413 .mode = BACKGROUND_MODE_SOLID_COLOR, 501 " -i, --image [<output>:]<path> "
414 .color = 0xFFFFFFFF, 502 "Display the given image.\n"
415 .show_indicator = true, 503 " -s, --scaling <mode> "
416 }; 504 "Scaling mode: stretch, fill, fit, center, tile.\n"
417 wl_list_init(&state.images); 505 " -t, --tiling "
418 506 "Same as --scaling=tile.\n"
419 wlr_log_init(L_DEBUG, NULL); 507 " -u, --no-unlock-indicator "
508 "Disable the unlock indicator.\n"
509 " -v, --version "
510 "Show the version number and quit.\n"
511 " --bs-hl-color <color> "
512 "Sets the color of backspace highlight segments.\n"
513 " --font <font> "
514 "Sets the font of the text.\n"
515 " --indicator-radius <radius> "
516 "Sets the indicator radius.\n"
517 " --indicator-thickness <thick> "
518 "Sets the indicator thickness.\n"
519 " --inside-color <color> "
520 "Sets the color of the inside of the indicator.\n"
521 " --inside-clear-color <color> "
522 "Sets the color of the inside of the indicator when cleared.\n"
523 " --inside-ver-color <color> "
524 "Sets the color of the inside of the indicator when verifying.\n"
525 " --inside-wrong-color <color> "
526 "Sets the color of the inside of the indicator when invalid.\n"
527 " --key-hl-color <color> "
528 "Sets the color of the key press highlight segments.\n"
529 " --line-color <color> "
530 "Sets the color of the line between the inside and ring.\n"
531 " --line-clear-color <color> "
532 "Sets the color of the line between the inside and ring when "
533 "cleared.\n"
534 " --line-ver-color <color> "
535 "Sets the color of the line between the inside and ring when "
536 "verifying.\n"
537 " --line-wrong-color <color> "
538 "Sets the color of the line between the inside and ring when "
539 "invalid.\n"
540 " -n, --line-uses-inside "
541 "Use the inside color for the line between the inside and ring.\n"
542 " -r, --line-uses-ring "
543 "Use the ring color for the line between the inside and ring.\n"
544 " --ring-color <color> "
545 "Sets the color of the ring of the indicator.\n"
546 " --ring-clear-color <color> "
547 "Sets the color of the ring of the indicator when cleared.\n"
548 " --ring-ver-color <color> "
549 "Sets the color of the ring of the indicator when verifying.\n"
550 " --ring-wrong-color <color> "
551 "Sets the color of the ring of the indicator when invalid.\n"
552 " --separator-color <color> "
553 "Sets the color of the lines that separate highlight segments.\n"
554 " --text-color <color> "
555 "Sets the color of the text.\n"
556 " --text-clear-color <color> "
557 "Sets the color of the text when cleared.\n"
558 " --text-ver-color <color> "
559 "Sets the color of the text when verifying.\n"
560 " --text-wrong-color <color> "
561 "Sets the color of the text when invalid.\n"
562 "\n"
563 "All <color> options are of the form <rrggbb[aa]>.\n";
420 564
421 int c; 565 int c;
566 optind = 1;
422 while (1) { 567 while (1) {
423 int option_index = 0; 568 int opt_idx = 0;
424 c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); 569 c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx);
425 if (c == -1) { 570 if (c == -1) {
426 break; 571 break;
427 } 572 }
428 switch (c) { 573 switch (c) {
429 case 'c': { 574 case 'C':
430 state.args.color = parse_color(optarg); 575 // Config file. This will have already been handled so just ignore.
431 state.args.mode = BACKGROUND_MODE_SOLID_COLOR; 576 break;
577 case 'c':
578 state->args.colors.background = parse_color(optarg);
579 state->args.mode = BACKGROUND_MODE_SOLID_COLOR;
580 break;
581 case 'e':
582 state->args.ignore_empty = true;
583 break;
584 case 'f':
585 state->args.daemonize = true;
432 break; 586 break;
433 }
434 case 'i': 587 case 'i':
435 load_image(optarg, &state); 588 load_image(optarg, state);
589 break;
590 case 'n':
591 *line_mode = LM_INSIDE;
592 break;
593 case 'r':
594 *line_mode = LM_RING;
436 break; 595 break;
437 case 's': 596 case 's':
438 state.args.mode = parse_background_mode(optarg); 597 state->args.mode = parse_background_mode(optarg);
439 if (state.args.mode == BACKGROUND_MODE_INVALID) { 598 if (state->args.mode == BACKGROUND_MODE_INVALID) {
440 return 1; 599 return 1;
441 } 600 }
442 break; 601 break;
443 case 't': 602 case 't':
444 state.args.mode = BACKGROUND_MODE_TILE; 603 state->args.mode = BACKGROUND_MODE_TILE;
604 break;
605 case 'u':
606 state->args.show_indicator = false;
445 break; 607 break;
446 case 'v': 608 case 'v':
447#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE 609#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
@@ -451,11 +613,72 @@ int main(int argc, char **argv) {
451 fprintf(stdout, "version unknown\n"); 613 fprintf(stdout, "version unknown\n");
452#endif 614#endif
453 return 0; 615 return 0;
454 case 'u': 616 case LO_BS_HL_COLOR:
455 state.args.show_indicator = false; 617 state->args.colors.bs_highlight = parse_color(optarg);
456 break; 618 break;
457 case 'f': 619 case LO_FONT:
458 state.args.daemonize = true; 620 free(state->args.font);
621 state->args.font = strdup(optarg);
622 break;
623 case LO_IND_RADIUS:
624 state->args.radius = strtol(optarg, NULL, 0);
625 break;
626 case LO_IND_THICKNESS:
627 state->args.thickness = strtol(optarg, NULL, 0);
628 break;
629 case LO_INSIDE_COLOR:
630 state->args.colors.inside.input = parse_color(optarg);
631 break;
632 case LO_INSIDE_CLEAR_COLOR:
633 state->args.colors.inside.cleared = parse_color(optarg);
634 break;
635 case LO_INSIDE_VER_COLOR:
636 state->args.colors.inside.verifying = parse_color(optarg);
637 break;
638 case LO_INSIDE_WRONG_COLOR:
639 state->args.colors.inside.wrong = parse_color(optarg);
640 break;
641 case LO_KEY_HL_COLOR:
642 state->args.colors.key_highlight = parse_color(optarg);
643 break;
644 case LO_LINE_COLOR:
645 state->args.colors.line.input = parse_color(optarg);
646 break;
647 case LO_LINE_CLEAR_COLOR:
648 state->args.colors.line.cleared = parse_color(optarg);
649 break;
650 case LO_LINE_VER_COLOR:
651 state->args.colors.line.verifying = parse_color(optarg);
652 break;
653 case LO_LINE_WRONG_COLOR:
654 state->args.colors.line.wrong = parse_color(optarg);
655 break;
656 case LO_RING_COLOR:
657 state->args.colors.ring.input = parse_color(optarg);
658 break;
659 case LO_RING_CLEAR_COLOR:
660 state->args.colors.ring.cleared = parse_color(optarg);
661 break;
662 case LO_RING_VER_COLOR:
663 state->args.colors.ring.verifying = parse_color(optarg);
664 break;
665 case LO_RING_WRONG_COLOR:
666 state->args.colors.ring.wrong = parse_color(optarg);
667 break;
668 case LO_SEP_COLOR:
669 state->args.colors.separator = parse_color(optarg);
670 break;
671 case LO_TEXT_COLOR:
672 state->args.colors.text.input = parse_color(optarg);
673 break;
674 case LO_TEXT_CLEAR_COLOR:
675 state->args.colors.text.cleared = parse_color(optarg);
676 break;
677 case LO_TEXT_VER_COLOR:
678 state->args.colors.text.verifying = parse_color(optarg);
679 break;
680 case LO_TEXT_WRONG_COLOR:
681 state->args.colors.text.wrong = parse_color(optarg);
459 break; 682 break;
460 default: 683 default:
461 fprintf(stderr, "%s", usage); 684 fprintf(stderr, "%s", usage);
@@ -463,6 +686,149 @@ int main(int argc, char **argv) {
463 } 686 }
464 } 687 }
465 688
689 return 0;
690}
691
692static bool file_exists(const char *path) {
693 return path && access(path, R_OK) != -1;
694}
695
696static char *get_config_path(void) {
697 static const char *config_paths[] = {
698 "$HOME/.swaylock/config",
699 "$XDG_CONFIG_HOME/swaylock/config",
700 SYSCONFDIR "/swaylock/config",
701 };
702
703 if (!getenv("XDG_CONFIG_HOME")) {
704 char *home = getenv("HOME");
705 char *config_home = malloc(strlen(home) + strlen("/.config") + 1);
706 if (!config_home) {
707 wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config");
708 } else {
709 strcpy(config_home, home);
710 strcat(config_home, "/.config");
711 setenv("XDG_CONFIG_HOME", config_home, 1);
712 wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home);
713 free(config_home);
714 }
715 }
716
717 wordexp_t p;
718 char *path;
719 for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) {
720 if (wordexp(config_paths[i], &p, 0) == 0) {
721 path = strdup(p.we_wordv[0]);
722 wordfree(&p);
723 if (file_exists(path)) {
724 return path;
725 }
726 free(path);
727 }
728 }
729
730 return NULL;
731}
732
733static int load_config(char *path, struct swaylock_state *state,
734 enum line_mode *line_mode) {
735 FILE *config = fopen(path, "r");
736 if (!config) {
737 wlr_log(WLR_ERROR, "Failed to read config. Running without it.");
738 return 0;
739 }
740 char *line;
741 int line_number = 0;
742 while (!feof(config)) {
743 line = read_line(config);
744 if (!line) {
745 continue;
746 }
747
748 line_number++;
749 if (line[0] == '#') {
750 free(line);
751 continue;
752 }
753 if (strlen(line) == 0) {
754 free(line);
755 continue;
756 }
757
758 wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line);
759 char flag[strlen(line) + 3];
760 sprintf(flag, "--%s", line);
761 char *argv[] = {"swaylock", flag};
762 int result = parse_options(2, argv, state, line_mode);
763 if (result != 0) {
764 free(line);
765 fclose(config);
766 return result;
767 }
768 free(line);
769 }
770 fclose(config);
771 return 0;
772}
773
774static struct swaylock_state state;
775
776int main(int argc, char **argv) {
777 enum line_mode line_mode = LM_LINE;
778 state.args = (struct swaylock_args){
779 .mode = BACKGROUND_MODE_SOLID_COLOR,
780 .font = strdup("sans-serif"),
781 .radius = 50,
782 .thickness = 10,
783 .ignore_empty = false,
784 .show_indicator = true,
785 };
786 wl_list_init(&state.images);
787 set_default_colors(&state.args.colors);
788
789 wlr_log_init(WLR_DEBUG, NULL);
790
791 char *config_path = NULL;
792 static struct option long_options[] = {
793 {"config", required_argument, NULL, 'C'},
794 {0, 0, 0, 0},
795 };
796 while (1) {
797 int c = getopt_long(argc, argv, "C:", long_options, NULL);
798 if (c == -1) {
799 break;
800 } else if (c == 'C') {
801 config_path = strdup(optarg);
802 break;
803 }
804 }
805 if (!config_path) {
806 config_path = get_config_path();
807 }
808
809 if (config_path) {
810 wlr_log(WLR_DEBUG, "Found config at %s", config_path);
811 int config_status = load_config(config_path, &state, &line_mode);
812 free(config_path);
813 if (config_status != 0) {
814 return config_status;
815 }
816 }
817
818 if (argc > 1) {
819 wlr_log(WLR_DEBUG, "Parsing CLI Args");
820 int result = parse_options(argc, argv, &state, &line_mode);
821 if (result != 0) {
822 return result;
823 }
824 }
825
826 if (line_mode == LM_INSIDE) {
827 state.args.colors.line = state.args.colors.inside;
828 } else if (line_mode == LM_RING) {
829 state.args.colors.line = state.args.colors.ring;
830 }
831
466#ifdef __linux__ 832#ifdef __linux__
467 // Most non-linux platforms require root to mlock() 833 // Most non-linux platforms require root to mlock()
468 if (mlock(state.password.buffer, sizeof(state.password.buffer)) != 0) { 834 if (mlock(state.password.buffer, sizeof(state.password.buffer)) != 0) {
@@ -480,13 +846,13 @@ int main(int argc, char **argv) {
480 wl_display_roundtrip(state.display); 846 wl_display_roundtrip(state.display);
481 assert(state.compositor && state.layer_shell && state.shm); 847 assert(state.compositor && state.layer_shell && state.shm);
482 if (!state.input_inhibit_manager) { 848 if (!state.input_inhibit_manager) {
483 wlr_log(L_ERROR, "Compositor does not support the input inhibitor " 849 wlr_log(WLR_ERROR, "Compositor does not support the input inhibitor "
484 "protocol, refusing to run insecurely"); 850 "protocol, refusing to run insecurely");
485 return 1; 851 return 1;
486 } 852 }
487 853
488 if (wl_list_empty(&state.surfaces)) { 854 if (wl_list_empty(&state.surfaces)) {
489 wlr_log(L_DEBUG, "Exiting - no outputs to show on."); 855 wlr_log(WLR_DEBUG, "Exiting - no outputs to show on.");
490 return 0; 856 return 0;
491 } 857 }
492 858
@@ -502,7 +868,7 @@ int main(int argc, char **argv) {
502 } 868 }
503 wl_display_roundtrip(state.display); 869 wl_display_roundtrip(state.display);
504 } else { 870 } else {
505 wlr_log(L_INFO, "Compositor does not support zxdg output manager, " 871 wlr_log(WLR_INFO, "Compositor does not support zxdg output manager, "
506 "images assigned to named outputs will not work"); 872 "images assigned to named outputs will not work");
507 } 873 }
508 874
@@ -520,5 +886,7 @@ int main(int argc, char **argv) {
520 while (wl_display_dispatch(state.display) != -1 && state.run_display) { 886 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
521 // This space intentionally left blank 887 // This space intentionally left blank
522 } 888 }
889
890 free(state.args.font);
523 return 0; 891 return 0;
524} 892}
diff --git a/swaylock/password.c b/swaylock/password.c
index d844ec98..7c686b34 100644
--- a/swaylock/password.c
+++ b/swaylock/password.c
@@ -53,15 +53,15 @@ static bool attempt_password(struct swaylock_password *pw) {
53 // TODO: only call pam_start once. keep the same handle the whole time 53 // TODO: only call pam_start once. keep the same handle the whole time
54 if ((pam_err = pam_start("swaylock", username, 54 if ((pam_err = pam_start("swaylock", username,
55 &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { 55 &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
56 wlr_log(L_ERROR, "PAM returned error %d", pam_err); 56 wlr_log(WLR_ERROR, "PAM returned error %d", pam_err);
57 } 57 }
58 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { 58 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
59 wlr_log(L_ERROR, "pam_authenticate failed"); 59 wlr_log(WLR_ERROR, "pam_authenticate failed");
60 goto fail; 60 goto fail;
61 } 61 }
62 // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand 62 // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand
63 if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { 63 if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
64 wlr_log(L_ERROR, "pam_end failed"); 64 wlr_log(WLR_ERROR, "pam_end failed");
65 goto fail; 65 goto fail;
66 } 66 }
67 clear_password_buffer(pw); 67 clear_password_buffer(pw);
@@ -95,6 +95,10 @@ void swaylock_handle_key(struct swaylock_state *state,
95 switch (keysym) { 95 switch (keysym) {
96 case XKB_KEY_KP_Enter: /* fallthrough */ 96 case XKB_KEY_KP_Enter: /* fallthrough */
97 case XKB_KEY_Return: 97 case XKB_KEY_Return:
98 if (state->args.ignore_empty && state->password.len == 0) {
99 break;
100 }
101
98 state->auth_state = AUTH_STATE_VALIDATING; 102 state->auth_state = AUTH_STATE_VALIDATING;
99 damage_state(state); 103 damage_state(state);
100 while (wl_display_dispatch(state->display) != -1 && state->run_display) { 104 while (wl_display_dispatch(state->display) != -1 && state->run_display) {
diff --git a/swaylock/render.c b/swaylock/render.c
index ea23d0d8..66c55965 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -7,11 +7,22 @@
7#include "swaylock/swaylock.h" 7#include "swaylock/swaylock.h"
8 8
9#define M_PI 3.14159265358979323846 9#define M_PI 3.14159265358979323846
10const int ARC_RADIUS = 50;
11const int ARC_THICKNESS = 10;
12const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; 10const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
13const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; 11const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
14 12
13static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state,
14 struct swaylock_colorset *colorset) {
15 if (state->auth_state == AUTH_STATE_VALIDATING) {
16 cairo_set_source_u32(cairo, colorset->verifying);
17 } else if (state->auth_state == AUTH_STATE_INVALID) {
18 cairo_set_source_u32(cairo, colorset->wrong);
19 } else if (state->auth_state == AUTH_STATE_CLEAR) {
20 cairo_set_source_u32(cairo, colorset->cleared);
21 } else {
22 cairo_set_source_u32(cairo, colorset->input);
23 }
24}
25
15void render_frame(struct swaylock_surface *surface) { 26void render_frame(struct swaylock_surface *surface) {
16 struct swaylock_state *state = surface->state; 27 struct swaylock_state *state = surface->state;
17 28
@@ -33,7 +44,7 @@ void render_frame(struct swaylock_surface *surface) {
33 cairo_save(cairo); 44 cairo_save(cairo);
34 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 45 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
35 if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { 46 if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) {
36 cairo_set_source_u32(cairo, state->args.color); 47 cairo_set_source_u32(cairo, state->args.colors.background);
37 cairo_paint(cairo); 48 cairo_paint(cairo);
38 } else { 49 } else {
39 render_background_image(cairo, surface->image, 50 render_background_image(cairo, surface->image,
@@ -42,49 +53,25 @@ void render_frame(struct swaylock_surface *surface) {
42 cairo_restore(cairo); 53 cairo_restore(cairo);
43 cairo_identity_matrix(cairo); 54 cairo_identity_matrix(cairo);
44 55
45 int arc_radius = ARC_RADIUS * surface->scale; 56 int arc_radius = state->args.radius * surface->scale;
46 int arc_thickness = ARC_THICKNESS * surface->scale; 57 int arc_thickness = state->args.thickness * surface->scale;
47 float type_indicator_border_thickness = 58 float type_indicator_border_thickness =
48 TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; 59 TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
49 60
50 if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { 61 if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) {
51 // Draw circle 62 // Draw circle
52 cairo_set_line_width(cairo, arc_thickness); 63 cairo_set_line_width(cairo, arc_thickness);
53 cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); 64 cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius,
54 switch (state->auth_state) { 65 0, 2 * M_PI);
55 case AUTH_STATE_INPUT: 66 set_color_for_state(cairo, state, &state->args.colors.inside);
56 case AUTH_STATE_INPUT_NOP: 67 cairo_fill_preserve(cairo);
57 case AUTH_STATE_BACKSPACE: { 68 set_color_for_state(cairo, state, &state->args.colors.ring);
58 cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); 69 cairo_stroke(cairo);
59 cairo_fill_preserve(cairo);
60 cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0);
61 cairo_stroke(cairo);
62 } break;
63 case AUTH_STATE_VALIDATING: {
64 cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75);
65 cairo_fill_preserve(cairo);
66 cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255);
67 cairo_stroke(cairo);
68 } break;
69 case AUTH_STATE_INVALID: {
70 cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75);
71 cairo_fill_preserve(cairo);
72 cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0);
73 cairo_stroke(cairo);
74 } break;
75 case AUTH_STATE_CLEAR: {
76 cairo_set_source_rgba(cairo, 229.0/255, 164.0/255, 69.0/255, 0.75);
77 cairo_fill_preserve(cairo);
78 cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255);
79 cairo_stroke(cairo);
80 } break;
81 default: break;
82 }
83 70
84 // Draw a message 71 // Draw a message
85 char *text = NULL; 72 char *text = NULL;
86 cairo_set_source_rgb(cairo, 0, 0, 0); 73 set_color_for_state(cairo, state, &state->args.colors.text);
87 cairo_select_font_face(cairo, "sans-serif", 74 cairo_select_font_face(cairo, state->args.font,
88 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); 75 CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
89 cairo_set_font_size(cairo, arc_radius / 3.0f); 76 cairo_set_font_size(cairo, arc_radius / 3.0f);
90 switch (state->auth_state) { 77 switch (state->auth_state) {
@@ -101,9 +88,10 @@ void render_frame(struct swaylock_surface *surface) {
101 case AUTH_STATE_INPUT_NOP: 88 case AUTH_STATE_INPUT_NOP:
102 if (state->xkb.caps_lock) { 89 if (state->xkb.caps_lock) {
103 text = "Caps Lock"; 90 text = "Caps Lock";
104 cairo_set_source_rgb(cairo, 229.0/255, 164.0/255, 69.0/255);
105 } 91 }
106 default: break; 92 break;
93 default:
94 break;
107 } 95 }
108 96
109 if (text) { 97 if (text) {
@@ -131,14 +119,14 @@ void render_frame(struct swaylock_surface *surface) {
131 arc_radius, highlight_start, 119 arc_radius, highlight_start,
132 highlight_start + TYPE_INDICATOR_RANGE); 120 highlight_start + TYPE_INDICATOR_RANGE);
133 if (state->auth_state == AUTH_STATE_INPUT) { 121 if (state->auth_state == AUTH_STATE_INPUT) {
134 cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); 122 cairo_set_source_u32(cairo, state->args.colors.key_highlight);
135 } else { 123 } else {
136 cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); 124 cairo_set_source_u32(cairo, state->args.colors.bs_highlight);
137 } 125 }
138 cairo_stroke(cairo); 126 cairo_stroke(cairo);
139 127
140 // Draw borders 128 // Draw borders
141 cairo_set_source_rgb(cairo, 0, 0, 0); 129 cairo_set_source_u32(cairo, state->args.colors.separator);
142 cairo_arc(cairo, buffer_width / 2, buffer_height / 2, 130 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
143 arc_radius, highlight_start, 131 arc_radius, highlight_start,
144 highlight_start + type_indicator_border_thickness); 132 highlight_start + type_indicator_border_thickness);
@@ -152,7 +140,7 @@ void render_frame(struct swaylock_surface *surface) {
152 } 140 }
153 141
154 // Draw inner + outer border of the circle 142 // Draw inner + outer border of the circle
155 cairo_set_source_rgb(cairo, 0, 0, 0); 143 set_color_for_state(cairo, state, &state->args.colors.line);
156 cairo_set_line_width(cairo, 2.0 * surface->scale); 144 cairo_set_line_width(cairo, 2.0 * surface->scale);
157 cairo_arc(cairo, buffer_width / 2, buffer_height / 2, 145 cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
158 arc_radius - arc_thickness / 2, 0, 2 * M_PI); 146 arc_radius - arc_thickness / 2, 0, 2 * M_PI);
diff --git a/swaylock/seat.c b/swaylock/seat.c
index 6c66d220..c2630d87 100644
--- a/swaylock/seat.c
+++ b/swaylock/seat.c
@@ -12,13 +12,13 @@ static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
12 struct swaylock_state *state = data; 12 struct swaylock_state *state = data;
13 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { 13 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
14 close(fd); 14 close(fd);
15 wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); 15 wlr_log(WLR_ERROR, "Unknown keymap format %d, aborting", format);
16 exit(1); 16 exit(1);
17 } 17 }
18 char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 18 char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
19 if (map_shm == MAP_FAILED) { 19 if (map_shm == MAP_FAILED) {
20 close(fd); 20 close(fd);
21 wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); 21 wlr_log(WLR_ERROR, "Unable to initialize keymap shm, aborting");
22 exit(1); 22 exit(1);
23 } 23 }
24 struct xkb_keymap *keymap = xkb_keymap_new_from_string( 24 struct xkb_keymap *keymap = xkb_keymap_new_from_string(
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd
index 1b3366f0..3107124f 100644
--- a/swaylock/swaylock.1.scd
+++ b/swaylock/swaylock.1.scd
@@ -12,23 +12,34 @@ Locks your Wayland session.
12 12
13# OPTIONS 13# OPTIONS
14 14
15*-h, --help* 15*-C, --config* <path>
16 Show help message and quit. 16 The config file to use. By default, the following paths are checked:
17 _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and
18 _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid
19 options in the configuration file using the format _long-option=value_.
20 For options such as _ignore-empty-password_, just supply the _long-option_.
21 All leading dashes should be omitted and the equals sign is required for
22 flags that take an argument.
17 23
18*-c, --color* <rrggbb[aa]> 24*-c, --color* <rrggbb[aa]>
19 Turn the screen into the given color. If -i is used, this sets the 25 Turn the screen into the given color. If -i is used, this sets the
20 background of the image to the given color. Defaults to white (FFFFFF), or 26 background of the image to the given color. Defaults to white (FFFFFF), or
21 transparent (00000000) if an image is in use. 27 transparent (00000000) if an image is in use.
22 28
29*-e, --ignore-empty-password*
30 When an empty password is provided by the user, do not validate it.
31
23*-f, --daemonize* 32*-f, --daemonize*
24 Fork into the background after spawning. Note: this is the default behavior 33 Detach from the controlling terminal after locking.
25 of i3lock. 34
35*-h, --help*
36 Show help message and quit.
26 37
27*-i, --image* [<output>:]<path> 38*-i, --image* [<output>:]<path>
28 Display the given image, optionally only on the given output. Use -c to set 39 Display the given image, optionally only on the given output. Use -c to set
29 a background color. 40 a background color.
30 41
31*--scaling* 42*-s, --scaling*
32 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. 43 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_.
33 44
34*-t, --tiling* 45*-t, --tiling*
@@ -37,37 +48,57 @@ Locks your Wayland session.
37*-u, --no-unlock-indicator* 48*-u, --no-unlock-indicator*
38 Disable the unlock indicator. 49 Disable the unlock indicator.
39 50
40*-f, --daemonize*
41 Detach from the controlling terminal after locking.
42
43*-v, --version* 51*-v, --version*
44 Show the version number and quit. 52 Show the version number and quit.
45 53
46# APPEARANCE 54# APPEARANCE
47 55
48*--bshlcolor* <rrggbb[aa]> 56*--bs-hl-color* <rrggbb[aa]>
49 Sets the color of backspace highlight segments. 57 Sets the color of backspace highlight segments.
50 58
51*--font* <font> 59*--font* <font>
52 Sets the font of the text inside the indicator. 60 Sets the font of the text inside the indicator.
53 61
54*--insidecolor* <rrggbb[aa]> 62*--indicator-radius* <radius>
63 Sets the radius of the indicator to _radius_ pixels. The default value is
64 50.
65
66*--indicator-thickness* <thickness>
67 Sets the thickness of the indicator to _thickness_ pixels. The default value
68 is 10.
69
70*--inside-color* <rrggbb[aa]>
55 Sets the color of the inside of the indicator when typing or idle. 71 Sets the color of the inside of the indicator when typing or idle.
56 72
57*--insidevercolor* <rrggbb[aa]> 73*--inside-clear-color* <rrggbb[aa]>
74 Sets the color of the inside of the indicator when cleared.
75
76*--inside-ver-color* <rrggbb[aa]>
58 Sets the color of the inside of the indicator when verifying. 77 Sets the color of the inside of the indicator when verifying.
59 78
60*--insidewrongcolor* <rrggbb[aa]> 79*--inside-wrong-color* <rrggbb[aa]>
61 Sets the color of the inside of the indicator when invalid. 80 Sets the color of the inside of the indicator when invalid.
62 81
63*--keyhlcolor* <rrggbb[aa]> 82*--key-hl-color* <rrggbb[aa]>
64 Sets the color of keypress highlight segments. 83 Sets the color of key press highlight segments.
84
85*--line-color* <rrggbb[aa]>
86 Sets the color of the lines that separate the inside and outside of the
87 indicator when typing or idle.
88
89*--line-clear-color* <rrggbb[aa]>
90 Sets the color of the lines that separate the inside and outside of the
91 indicator when cleared.
92
93*--line-ver-color* <rrggbb[aa]>
94 Sets the color of the lines that separate the inside and outside of the
95 indicator when verifying.
65 96
66*--linecolor* <rrggbb[aa]> 97*--line-wrong-color* <rrggbb[aa]>
67 Sets the color of the lines that separate the inside and outside of the 98 Sets the color of the lines that separate the inside and outside of the
68 indicator. 99 indicator when invalid.
69 100
70*-s, --line-uses-inside* 101*-n, --line-uses-inside*
71 Use the color of the inside of the indicator for the line separating the 102 Use the color of the inside of the indicator for the line separating the
72 inside and outside of the indicator. 103 inside and outside of the indicator.
73 104
@@ -75,28 +106,32 @@ Locks your Wayland session.
75 Use the outer ring's color for the line separating the inside and outside of 106 Use the outer ring's color for the line separating the inside and outside of
76 the indicator. 107 the indicator.
77 108
78*--ringcolor* <rrggbb[aa]> 109*--ring-color* <rrggbb[aa]>
79 Sets the color of the outside of the indicator when typing or idle. 110 Sets the color of the outside of the indicator when typing or idle.
80 111
81*--ringvercolor* <rrggbb[aa]> 112*--ring-clear-color* <rrggbb[aa]>
113 Sets the color of the outside of the indicator when cleared.
114
115*--ring-ver-color* <rrggbb[aa]>
82 Sets the color of the outside of the indicator when verifying. 116 Sets the color of the outside of the indicator when verifying.
83 117
84*--ringwrongcolor* <rrggbb[aa]> 118*--ring-wrong-color* <rrggbb[aa]>
85 Sets the color of the outside of the indicator when invalid. 119 Sets the color of the outside of the indicator when invalid.
86 120
87*--separatorcolor* <rrggbb[aa]> 121*--separator-color* <rrggbb[aa]>
88 Sets the color of the lines that seperate highlight segments. 122 Sets the color of the lines that separate highlight segments.
89 123
90*--textcolor* <rrggbb[aa]> 124*--text-color* <rrggbb[aa]>
91 Sets the color of the text inside the indicator. 125 Sets the color of the text inside the indicator when typing or idle.
92 126
93*--indicator-radius* <radius> 127*--text-clear-color* <rrggbb[aa]>
94 Sets the radius of the indicator to _radius_ pixels. The default value is 128 Sets the color of the text inside the indicator when cleared.
95 50.
96 129
97*--indicator-thickness* <thickness> 130*--text-ver-color* <rrggbb[aa]>
98 Sets the thickness of the indicator to _thickness_ pixels. The default value 131 Sets the color of the text inside the indicator when verifying.
99 is 10. 132
133*--text-wrong-color* <rrggbb[aa]>
134 Sets the color of the text inside the indicator when invalid.
100 135
101# AUTHORS 136# AUTHORS
102 137
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 4283bf00..c4141ca5 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -240,44 +240,17 @@ static void pretty_print_version(json_object *v) {
240 printf("sway version %s\n", json_object_get_string(ver)); 240 printf("sway version %s\n", json_object_get_string(ver));
241} 241}
242 242
243static void pretty_print_clipboard(json_object *v) { 243static void pretty_print_config(json_object *c) {
244 if (success(v, true)) { 244 json_object *config;
245 if (json_object_is_type(v, json_type_array)) { 245 json_object_object_get_ex(c, "config", &config);
246 for (size_t i = 0; i < json_object_array_length(v); ++i) { 246 printf("%s\n", json_object_get_string(config));
247 json_object *o = json_object_array_get_idx(v, i);
248 printf("%s\n", json_object_get_string(o));
249 }
250 } else {
251 // NOTE: could be extended to print all received types
252 // instead just the first one when sways ipc server
253 // supports it
254 struct json_object_iterator iter = json_object_iter_begin(v);
255 struct json_object_iterator end = json_object_iter_end(v);
256 if (!json_object_iter_equal(&iter, &end)) {
257 json_object *obj = json_object_iter_peek_value(&iter);
258 if (success(obj, false)) {
259 json_object *content;
260 json_object_object_get_ex(obj, "content", &content);
261 printf("%s\n", json_object_get_string(content));
262 } else {
263 json_object *error;
264 json_object_object_get_ex(obj, "error", &error);
265 printf("Error: %s\n", json_object_get_string(error));
266 }
267 }
268 }
269 } else {
270 json_object *error;
271 json_object_object_get_ex(v, "error", &error);
272 printf("Error: %s\n", json_object_get_string(error));
273 }
274} 247}
275 248
276static void pretty_print(int type, json_object *resp) { 249static void pretty_print(int type, json_object *resp) {
277 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && 250 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES &&
278 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && 251 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS &&
279 type != IPC_GET_VERSION && type != IPC_GET_CLIPBOARD && 252 type != IPC_GET_VERSION && type != IPC_GET_SEATS &&
280 type != IPC_GET_SEATS) { 253 type != IPC_GET_CONFIG) {
281 printf("%s\n", json_object_to_json_string_ext(resp, 254 printf("%s\n", json_object_to_json_string_ext(resp,
282 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); 255 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
283 return; 256 return;
@@ -288,8 +261,8 @@ static void pretty_print(int type, json_object *resp) {
288 return; 261 return;
289 } 262 }
290 263
291 if (type == IPC_GET_CLIPBOARD) { 264 if (type == IPC_GET_CONFIG) {
292 pretty_print_clipboard(resp); 265 pretty_print_config(resp);
293 return; 266 return;
294 } 267 }
295 268
@@ -323,7 +296,7 @@ int main(int argc, char **argv) {
323 char *socket_path = NULL; 296 char *socket_path = NULL;
324 char *cmdtype = NULL; 297 char *cmdtype = NULL;
325 298
326 wlr_log_init(L_INFO, NULL); 299 wlr_log_init(WLR_INFO, NULL);
327 300
328 static struct option long_options[] = { 301 static struct option long_options[] = {
329 {"help", no_argument, NULL, 'h'}, 302 {"help", no_argument, NULL, 'h'},
@@ -407,8 +380,10 @@ int main(int argc, char **argv) {
407 type = IPC_GET_BAR_CONFIG; 380 type = IPC_GET_BAR_CONFIG;
408 } else if (strcasecmp(cmdtype, "get_version") == 0) { 381 } else if (strcasecmp(cmdtype, "get_version") == 0) {
409 type = IPC_GET_VERSION; 382 type = IPC_GET_VERSION;
410 } else if (strcasecmp(cmdtype, "get_clipboard") == 0) { 383 } else if (strcasecmp(cmdtype, "get_binding_modes") == 0) {
411 type = IPC_GET_CLIPBOARD; 384 type = IPC_GET_BINDING_MODES;
385 } else if (strcasecmp(cmdtype, "get_config") == 0) {
386 type = IPC_GET_CONFIG;
412 } else { 387 } else {
413 sway_abort("Unknown message type %s", cmdtype); 388 sway_abort("Unknown message type %s", cmdtype);
414 } 389 }
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index 1aa6a1b0..a6e279da 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -59,8 +59,8 @@ _swaymsg_ [options...] [message]
59*get\_version* 59*get\_version*
60 Get JSON-encoded version information for the running instance of sway. 60 Get JSON-encoded version information for the running instance of sway.
61 61
62*get\_clipboard* 62*get\_binding\_modes*
63 Get JSON-encoded information about the clipboard. 63 Gets a JSON-encoded list of currently configured binding modes.
64 Returns the current clipboard mime-types if called without 64
65 arguments, otherwise returns the clipboard data in the requested 65*get\_config*
66 formats. Encodes the data using base64 for non-text mime types. 66 Gets a JSON-encoded copy of the current configuration.