summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.ja.md2
-rw-r--r--common/background-image.c2
-rw-r--r--common/cairo.c16
-rw-r--r--common/pango.c58
-rw-r--r--contrib/awesome.config63
-rw-r--r--include/cairo.h2
-rw-r--r--include/pango.h14
-rw-r--r--include/sway/commands.h12
-rw-r--r--include/sway/config.h28
-rw-r--r--include/sway/debug.h2
-rw-r--r--include/sway/ipc-json.h2
-rw-r--r--include/sway/tree/container.h3
-rw-r--r--include/sway/tree/workspace.h9
-rw-r--r--include/swaybar/bar.h8
-rw-r--r--include/swaybar/config.h2
-rw-r--r--include/swaybar/i3bar.h34
-rw-r--r--include/swaybar/ipc.h2
-rw-r--r--include/swaybar/render.h4
-rw-r--r--include/swaybar/status_line.h26
-rw-r--r--include/swaybar/tray/dbus.h18
-rw-r--r--include/swaybar/tray/sni_watcher.h10
-rw-r--r--include/swaybar/tray/tray.h32
-rw-r--r--include/swaylock/swaylock.h4
-rw-r--r--meson.build25
-rw-r--r--meson_options.txt3
-rw-r--r--sway/commands.c45
-rw-r--r--sway/commands/bar/binding_mode_indicator.c6
-rw-r--r--sway/commands/bind.c2
-rw-r--r--sway/commands/gaps.c306
-rw-r--r--sway/commands/input.c11
-rw-r--r--sway/commands/input/accel_profile.c13
-rw-r--r--sway/commands/input/click_method.c15
-rw-r--r--sway/commands/input/drag.c26
-rw-r--r--sway/commands/input/drag_lock.c12
-rw-r--r--sway/commands/input/dwt.c12
-rw-r--r--sway/commands/input/events.c17
-rw-r--r--sway/commands/input/left_handed.c10
-rw-r--r--sway/commands/input/map_from_region.c42
-rw-r--r--sway/commands/input/map_to_output.c10
-rw-r--r--sway/commands/input/middle_emulation.c13
-rw-r--r--sway/commands/input/natural_scroll.c10
-rw-r--r--sway/commands/input/pointer_accel.c11
-rw-r--r--sway/commands/input/repeat_delay.c11
-rw-r--r--sway/commands/input/repeat_rate.c11
-rw-r--r--sway/commands/input/scroll_button.c13
-rw-r--r--sway/commands/input/scroll_method.c17
-rw-r--r--sway/commands/input/tap.c14
-rw-r--r--sway/commands/input/tap_button_map.c13
-rw-r--r--sway/commands/input/xkb_capslock.c13
-rw-r--r--sway/commands/input/xkb_layout.c17
-rw-r--r--sway/commands/input/xkb_model.c17
-rw-r--r--sway/commands/input/xkb_numlock.c13
-rw-r--r--sway/commands/input/xkb_options.c17
-rw-r--r--sway/commands/input/xkb_rules.c17
-rw-r--r--sway/commands/input/xkb_variant.c17
-rw-r--r--sway/commands/layout.c3
-rw-r--r--sway/commands/move.c64
-rw-r--r--sway/commands/output/background.c18
-rw-r--r--sway/commands/resize.c30
-rw-r--r--sway/commands/workspace.c74
-rw-r--r--sway/config.c11
-rw-r--r--sway/config/bar.c26
-rw-r--r--sway/config/input.c62
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/debug-tree.c2
-rw-r--r--sway/desktop/transaction.c66
-rw-r--r--sway/desktop/xdg_shell.c2
-rw-r--r--sway/desktop/xdg_shell_v6.c2
-rw-r--r--sway/desktop/xwayland.c2
-rw-r--r--sway/input/cursor.c62
-rw-r--r--sway/input/input-manager.c22
-rw-r--r--sway/input/seat.c44
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/ipc-server.c15
-rw-r--r--sway/main.c84
-rw-r--r--sway/meson.build3
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway.5.scd82
-rw-r--r--sway/tree/arrange.c3
-rw-r--r--sway/tree/container.c38
-rw-r--r--sway/tree/output.c30
-rw-r--r--sway/tree/root.c21
-rw-r--r--sway/tree/view.c29
-rw-r--r--sway/tree/workspace.c81
-rw-r--r--swaybar/bar.c161
-rw-r--r--swaybar/config.c2
-rw-r--r--swaybar/i3bar.c19
-rw-r--r--swaybar/ipc.c19
-rw-r--r--swaybar/main.c5
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/render.c146
-rw-r--r--swaybar/status_line.c36
-rw-r--r--swaybg/main.c4
-rw-r--r--swaybg/meson.build1
-rw-r--r--swayidle/main.c4
-rw-r--r--swayidle/meson.build1
-rw-r--r--swaylock/main.c15
-rw-r--r--swaylock/meson.build51
-rw-r--r--swaylock/pam.c62
-rw-r--r--swaylock/password.c51
-rw-r--r--swaylock/render.c7
-rw-r--r--swaylock/shadow.c128
-rw-r--r--swaymsg/meson.build1
-rw-r--r--swaynag/config.c2
-rw-r--r--swaynag/meson.build1
107 files changed, 1477 insertions, 1266 deletions
diff --git a/README.ja.md b/README.ja.md
index 75d29c73..b0488c53 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -5,7 +5,7 @@ i3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。
5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。
6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。
7 7
8**注意**: Swayは現在*凍結中*であり、Swayとwlrootsの統合が完了するまで、新たな機能は追加されません。バグフィックスは行われます。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。 8**注意**: Swayは現在*凍結中*であり、wlcからwlrootsへの移植が完了するまで新たな機能は追加されません。2018年9月以降に発見されるバグは0.15では対応されません。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。
9 9
10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
11 11
diff --git a/common/background-image.c b/common/background-image.c
index f3d2551e..5ede55e3 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -58,6 +58,7 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image,
58 double width = cairo_image_surface_get_width(image); 58 double width = cairo_image_surface_get_width(image);
59 double height = cairo_image_surface_get_height(image); 59 double height = cairo_image_surface_get_height(image);
60 60
61 cairo_save(cairo);
61 switch (mode) { 62 switch (mode) {
62 case BACKGROUND_MODE_STRETCH: 63 case BACKGROUND_MODE_STRETCH:
63 cairo_scale(cairo, 64 cairo_scale(cairo,
@@ -116,4 +117,5 @@ void render_background_image(cairo_t *cairo, cairo_surface_t *image,
116 break; 117 break;
117 } 118 }
118 cairo_paint(cairo); 119 cairo_paint(cairo);
120 cairo_restore(cairo);
119} 121}
diff --git a/common/cairo.c b/common/cairo.c
index c267c77c..e8231484 100644
--- a/common/cairo.c
+++ b/common/cairo.c
@@ -13,6 +13,22 @@ void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
13 (color >> (0*8) & 0xFF) / 255.0); 13 (color >> (0*8) & 0xFF) / 255.0);
14} 14}
15 15
16cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) {
17 switch (subpixel) {
18 case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
19 return CAIRO_SUBPIXEL_ORDER_RGB;
20 case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
21 return CAIRO_SUBPIXEL_ORDER_BGR;
22 case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
23 return CAIRO_SUBPIXEL_ORDER_VRGB;
24 case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
25 return CAIRO_SUBPIXEL_ORDER_VBGR;
26 default:
27 return CAIRO_SUBPIXEL_ORDER_DEFAULT;
28 }
29 return CAIRO_SUBPIXEL_ORDER_DEFAULT;
30}
31
16cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, 32cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
17 int width, int height) { 33 int width, int height) {
18 int image_width = cairo_image_surface_get_width(image); 34 int image_width = cairo_image_surface_get_width(image);
diff --git a/common/pango.c b/common/pango.c
index ea71ac4a..ba74692e 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -6,67 +6,47 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "cairo.h"
9#include "log.h" 10#include "log.h"
11#include "stringop.h"
10 12
11int escape_markup_text(const char *src, char *dest, int dest_length) { 13size_t escape_markup_text(const char *src, char *dest) {
12 int length = 0; 14 size_t length = 0;
15 if (dest) {
16 dest[0] = '\0';
17 }
13 18
14 while (src[0]) { 19 while (src[0]) {
15 switch (src[0]) { 20 switch (src[0]) {
16 case '&': 21 case '&':
17 length += 5; 22 length += 5;
18 if (dest && dest_length - length >= 0) { 23 lenient_strcat(dest, "&amp;");
19 dest += sprintf(dest, "%s", "&amp;");
20 } else {
21 dest_length = -1;
22 }
23 break; 24 break;
24 case '<': 25 case '<':
25 length += 4; 26 length += 4;
26 if (dest && dest_length - length >= 0) { 27 lenient_strcat(dest, "&lt;");
27 dest += sprintf(dest, "%s", "&lt;");
28 } else {
29 dest_length = -1;
30 }
31 break; 28 break;
32 case '>': 29 case '>':
33 length += 4; 30 length += 4;
34 if (dest && dest_length - length >= 0) { 31 lenient_strcat(dest, "&gt;");
35 dest += sprintf(dest, "%s", "&gt;");
36 } else {
37 dest_length = -1;
38 }
39 break; 32 break;
40 case '\'': 33 case '\'':
41 length += 6; 34 length += 6;
42 if (dest && dest_length - length >= 0) { 35 lenient_strcat(dest, "&apos;");
43 dest += sprintf(dest, "%s", "&apos;");
44 } else {
45 dest_length = -1;
46 }
47 break; 36 break;
48 case '"': 37 case '"':
49 length += 6; 38 length += 6;
50 if (dest && dest_length - length >= 0) { 39 lenient_strcat(dest, "&quot;");
51 dest += sprintf(dest, "%s", "&quot;");
52 } else {
53 dest_length = -1;
54 }
55 break; 40 break;
56 default: 41 default:
57 length += 1; 42 if (dest) {
58 if (dest && dest_length - length >= 0) { 43 dest[length] = *src;
59 *(dest++) = *src; 44 dest[length + 1] = '\0';
60 } else {
61 dest_length = -1;
62 } 45 }
46 length += 1;
63 } 47 }
64 src++; 48 src++;
65 } 49 }
66 // if we could not fit the escaped string in dest, return -1
67 if (dest && dest_length == -1) {
68 return -1;
69 }
70 return length; 50 return length;
71} 51}
72 52
@@ -78,7 +58,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
78 char *buf; 58 char *buf;
79 GError *error = NULL; 59 GError *error = NULL;
80 if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { 60 if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {
81 pango_layout_set_markup(layout, buf, -1); 61 pango_layout_set_text(layout, buf, -1);
82 free(buf); 62 free(buf);
83 } else { 63 } else {
84 wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text, 64 wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text,
@@ -134,6 +114,10 @@ void pango_printf(cairo_t *cairo, const char *font,
134 va_end(args); 114 va_end(args);
135 115
136 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 116 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
117 cairo_font_options_t *fo = cairo_font_options_create();
118 cairo_get_font_options(cairo, fo);
119 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
120 cairo_font_options_destroy(fo);
137 pango_cairo_update_layout(cairo, layout); 121 pango_cairo_update_layout(cairo, layout);
138 pango_cairo_show_layout(cairo, layout); 122 pango_cairo_show_layout(cairo, layout);
139 g_object_unref(layout); 123 g_object_unref(layout);
diff --git a/contrib/awesome.config b/contrib/awesome.config
deleted file mode 100644
index 41e17c91..00000000
--- a/contrib/awesome.config
+++ /dev/null
@@ -1,63 +0,0 @@
1#
2# Replicate some of Awesome's default layout manipulation configuration for Sway
3#
4# Differences:
5# - Layout switching doesn't use the spacebar (i.e. i3/Sway behavior to switch to/from floating windows)
6# and uses the 'A' key instead (as in auto)
7# - Resizing windows uses i3/Sway's more versatile Mod4+r
8# - no tags
9# - no Maximize/Minize, alternatives to Maximize would be to switch to Stacked/Tabbed layouts
10# via Mod4+w or Mod4+s.
11# - kill focused client is available on Mod4+Shift+q (instead of Mod4+Shift+c, which maps to Sway's
12# config reload)
13# - probably many more ...
14
15# Awesome-style container traversal using Vim-like binding
16set $next j
17set $prev k
18
19#
20# Moving around:
21#
22 # Move your focus around
23 bindsym $mod+$next focus next
24 bindsym $mod+$prev focus prev
25
26 # _move_ the focused window with the same, but add Shift
27 bindsym $mod+Shift+$next move next
28 bindsym $mod+Shift+$prev move prev
29
30#
31# Layout:
32#
33 workspace_layout auto left
34
35 # This is usually bound to $mod+space, but this works well in practice by keeping
36 # all the layout switching keys grouped together.
37 bindsym $mod+a layout auto next
38 bindsym $mod+Shift+a layout auto prev
39
40 # Promote a child to master position in an auto layout
41 bindsym $mod+Control+Return move first
42
43 # Increase/decrease number of master elements in auto layout
44 bindsym $mod+Shift+h layout auto master inc 1
45 bindsym $mod+Shift+l layout auto master inc -1
46
47 # Increase/decrease number of slave element groups in auto layout
48 bindsym $mod+Control+h layout auto ncol inc 1
49 bindsym $mod+Control+l layout auto ncol inc -1
50
51#
52# Resizing containers:
53# Again, not really the way Awesome works well, but in spirit with i3/Sway and it works well.
54#
55mode "resize" {
56 bindsym Left resize shrink width 20 px
57 bindsym Down resize grow height 20 px
58 bindsym Up resize shrink height 20 px
59 bindsym Right resize grow width 20 px
60}
61bindsym $mod+r mode "resize"
62
63new_window pixel 1
diff --git a/include/cairo.h b/include/cairo.h
index 31672705..86530b60 100644
--- a/include/cairo.h
+++ b/include/cairo.h
@@ -2,8 +2,10 @@
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_H
3#include <stdint.h> 3#include <stdint.h>
4#include <cairo/cairo.h> 4#include <cairo/cairo.h>
5#include <wlr/types/wlr_output.h>
5 6
6void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 7void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
8cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel);
7 9
8cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, 10cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
9 int width, int height); 11 int width, int height);
diff --git a/include/pango.h b/include/pango.h
index 09a535a5..6ab83c16 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -6,17 +6,13 @@
6#include <cairo/cairo.h> 6#include <cairo/cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8 8
9/* Utility function which escape characters a & < > ' ". 9/**
10 * Utility function which escape characters a & < > ' ".
10 * 11 *
11 * If the dest parameter is NULL, then the function returns the length of 12 * The function returns the length of the escaped string, optionally writing the
12 * of the escaped src string. The dest_length doesn't matter. 13 * escaped string to dest if provided.
13 *
14 * If the dest parameter is not NULL then the fuction escapes the src string
15 * an puts the escaped string in dest and returns the lenght of the escaped string.
16 * The dest_length parameter is the size of dest array. If the size of dest is not
17 * enough, then the function returns -1.
18 */ 14 */
19int escape_markup_text(const char *src, char *dest, int dest_length); 15size_t escape_markup_text(const char *src, char *dest);
20PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 16PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
21 const char *text, double scale, bool markup); 17 const char *text, double scale, bool markup);
22void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 18void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 226cf932..afa65340 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -4,6 +4,8 @@
4#include <wlr/util/edges.h> 4#include <wlr/util/edges.h>
5#include "config.h" 5#include "config.h"
6 6
7struct sway_container;
8
7typedef struct cmd_results *sway_cmd(int argc, char **argv); 9typedef struct cmd_results *sway_cmd(int argc, char **argv);
8 10
9struct cmd_handler { 11struct cmd_handler {
@@ -50,8 +52,13 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
50 int handlers_size); 52 int handlers_size);
51/** 53/**
52 * Parse and executes a command. 54 * Parse and executes a command.
55 *
56 * If the command string contains criteria then the command will be executed on
57 * all matching containers. Otherwise, it'll run on the `con` container. If
58 * `con` is NULL then it'll run on the currently focused container.
53 */ 59 */
54struct cmd_results *execute_command(char *command, struct sway_seat *seat); 60struct cmd_results *execute_command(char *command, struct sway_seat *seat,
61 struct sway_container *con);
55/** 62/**
56 * Parse and handles a command during config file loading. 63 * Parse and handles a command during config file loading.
57 * 64 *
@@ -88,7 +95,7 @@ struct cmd_results *add_color(const char *name,
88/** 95/**
89 * TODO: Move this function and its dependent functions to container.c. 96 * TODO: Move this function and its dependent functions to container.c.
90 */ 97 */
91void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, 98bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge,
92 int amount); 99 int amount);
93 100
94sway_cmd cmd_assign; 101sway_cmd cmd_assign;
@@ -210,6 +217,7 @@ sway_cmd bar_colors_cmd_urgent_workspace;
210sway_cmd input_cmd_seat; 217sway_cmd input_cmd_seat;
211sway_cmd input_cmd_accel_profile; 218sway_cmd input_cmd_accel_profile;
212sway_cmd input_cmd_click_method; 219sway_cmd input_cmd_click_method;
220sway_cmd input_cmd_drag;
213sway_cmd input_cmd_drag_lock; 221sway_cmd input_cmd_drag_lock;
214sway_cmd input_cmd_dwt; 222sway_cmd input_cmd_dwt;
215sway_cmd input_cmd_events; 223sway_cmd input_cmd_events;
diff --git a/include/sway/config.h b/include/sway/config.h
index b53c1f1f..eab48aed 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -93,6 +93,7 @@ struct input_config {
93 93
94 int accel_profile; 94 int accel_profile;
95 int click_method; 95 int click_method;
96 int drag;
96 int drag_lock; 97 int drag_lock;
97 int dwt; 98 int dwt;
98 int left_handed; 99 int left_handed;
@@ -167,13 +168,14 @@ struct output_config {
167}; 168};
168 169
169/** 170/**
170 * Maps a workspace name to an output name. 171 * Stores configuration for a workspace, regardless of whether the workspace
171 * 172 * exists.
172 * Set via `workspace <x> output <y>`
173 */ 173 */
174struct workspace_output { 174struct workspace_config {
175 char *output;
176 char *workspace; 175 char *workspace;
176 char *output;
177 int gaps_inner;
178 int gaps_outer;
177}; 179};
178 180
179struct bar_config { 181struct bar_config {
@@ -327,7 +329,7 @@ struct sway_config {
327 list_t *modes; 329 list_t *modes;
328 list_t *bars; 330 list_t *bars;
329 list_t *cmd_queue; 331 list_t *cmd_queue;
330 list_t *workspace_outputs; 332 list_t *workspace_configs;
331 list_t *output_configs; 333 list_t *output_configs;
332 list_t *input_configs; 334 list_t *input_configs;
333 list_t *seat_configs; 335 list_t *seat_configs;
@@ -450,20 +452,16 @@ void free_sway_variable(struct sway_variable *var);
450 */ 452 */
451char *do_var_replacement(char *str); 453char *do_var_replacement(char *str);
452 454
453struct cmd_results *check_security_config();
454
455int input_identifier_cmp(const void *item, const void *data); 455int input_identifier_cmp(const void *item, const void *data);
456 456
457struct input_config *new_input_config(const char* identifier); 457struct input_config *new_input_config(const char* identifier);
458 458
459void merge_input_config(struct input_config *dst, struct input_config *src); 459void merge_input_config(struct input_config *dst, struct input_config *src);
460 460
461struct input_config *copy_input_config(struct input_config *ic); 461struct input_config *store_input_config(struct input_config *ic);
462 462
463void free_input_config(struct input_config *ic); 463void free_input_config(struct input_config *ic);
464 464
465void apply_input_config(struct input_config *input);
466
467int seat_name_cmp(const void *item, const void *data); 465int seat_name_cmp(const void *item, const void *data);
468 466
469struct seat_config *new_seat_config(const char* name); 467struct seat_config *new_seat_config(const char* name);
@@ -474,7 +472,7 @@ struct seat_config *copy_seat_config(struct seat_config *seat);
474 472
475void free_seat_config(struct seat_config *ic); 473void free_seat_config(struct seat_config *ic);
476 474
477struct seat_attachment_config *seat_attachment_config_new(); 475struct seat_attachment_config *seat_attachment_config_new(void);
478 476
479struct seat_attachment_config *seat_config_get_attachment( 477struct seat_attachment_config *seat_config_get_attachment(
480 struct seat_config *seat_config, char *identifier); 478 struct seat_config *seat_config, char *identifier);
@@ -512,9 +510,7 @@ void free_sway_binding(struct sway_binding *sb);
512 510
513void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 511void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
514 512
515void load_swaybars(); 513void load_swaybars(void);
516
517void invoke_swaybar(struct bar_config *bar);
518 514
519void terminate_swaybg(pid_t pid); 515void terminate_swaybg(pid_t pid);
520 516
@@ -522,6 +518,8 @@ struct bar_config *default_bar_config(void);
522 518
523void free_bar_config(struct bar_config *bar); 519void free_bar_config(struct bar_config *bar);
524 520
521void free_workspace_config(struct workspace_config *wsc);
522
525/** 523/**
526 * Updates the value of config->font_height based on the max title height 524 * Updates the value of config->font_height based on the max title height
527 * reported by each container. If recalculate is true, the containers will 525 * reported by each container. If recalculate is true, the containers will
diff --git a/include/sway/debug.h b/include/sway/debug.h
index bf3a5f6d..0e9bb056 100644
--- a/include/sway/debug.h
+++ b/include/sway/debug.h
@@ -17,6 +17,6 @@ struct sway_debug {
17 17
18extern struct sway_debug debug; 18extern struct sway_debug debug;
19 19
20void update_debug_tree(); 20void update_debug_tree(void);
21 21
22#endif 22#endif
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h
index fef243e3..1cbfd15d 100644
--- a/include/sway/ipc-json.h
+++ b/include/sway/ipc-json.h
@@ -4,7 +4,7 @@
4#include "sway/tree/container.h" 4#include "sway/tree/container.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6 6
7json_object *ipc_json_get_version(); 7json_object *ipc_json_get_version(void);
8 8
9json_object *ipc_json_describe_disabled_output(struct sway_output *o); 9json_object *ipc_json_describe_disabled_output(struct sway_output *o);
10json_object *ipc_json_describe_node(struct sway_node *node); 10json_object *ipc_json_describe_node(struct sway_node *node);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 5e281a2f..b865a0f2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -94,9 +94,6 @@ struct sway_container {
94 94
95 // The gaps currently applied to the container. 95 // The gaps currently applied to the container.
96 double current_gaps; 96 double current_gaps;
97 bool has_gaps;
98 double gaps_inner;
99 double gaps_outer;
100 97
101 struct sway_workspace *workspace; // NULL when hidden in the scratchpad 98 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
102 struct sway_container *parent; // NULL if container in root of workspace 99 struct sway_container *parent; // NULL if container in root of workspace
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index e4b616d1..efdae5a1 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -32,10 +32,9 @@ struct sway_workspace {
32 enum sway_container_layout layout; 32 enum sway_container_layout layout;
33 enum sway_container_layout prev_split_layout; 33 enum sway_container_layout prev_split_layout;
34 34
35 double current_gaps; 35 int current_gaps;
36 bool has_gaps; 36 int gaps_inner;
37 double gaps_inner; 37 int gaps_outer;
38 double gaps_outer;
39 38
40 struct sway_output *output; // NULL if no outputs are connected 39 struct sway_output *output; // NULL if no outputs are connected
41 list_t *floating; // struct sway_container 40 list_t *floating; // struct sway_container
@@ -48,6 +47,8 @@ struct sway_workspace {
48 47
49extern char *prev_workspace_name; 48extern char *prev_workspace_name;
50 49
50struct workspace_config *workspace_find_config(const char *ws_name);
51
51struct sway_output *workspace_get_initial_output(const char *name); 52struct sway_output *workspace_get_initial_output(const char *name);
52 53
53struct sway_workspace *workspace_create(struct sway_output *output, 54struct sway_workspace *workspace_create(struct sway_output *output,
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 4065fb8b..de234111 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -54,7 +54,6 @@ struct swaybar {
54 struct wl_seat *seat; 54 struct wl_seat *seat;
55 55
56 struct swaybar_config *config; 56 struct swaybar_config *config;
57 struct swaybar_output *focused_output;
58 struct swaybar_pointer pointer; 57 struct swaybar_pointer pointer;
59 struct status_line *status; 58 struct status_line *status;
60 59
@@ -81,8 +80,11 @@ struct swaybar_output {
81 80
82 uint32_t width, height; 81 uint32_t width, height;
83 int32_t scale; 82 int32_t scale;
83 enum wl_output_subpixel subpixel;
84 struct pool_buffer buffers[2]; 84 struct pool_buffer buffers[2];
85 struct pool_buffer *current_buffer; 85 struct pool_buffer *current_buffer;
86 bool dirty;
87 bool frame_scheduled;
86}; 88};
87 89
88struct swaybar_workspace { 90struct swaybar_workspace {
@@ -94,9 +96,7 @@ struct swaybar_workspace {
94 bool urgent; 96 bool urgent;
95}; 97};
96 98
97void bar_setup(struct swaybar *bar, 99bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id);
98 const char *socket_path,
99 const char *bar_id);
100void bar_run(struct swaybar *bar); 100void bar_run(struct swaybar *bar);
101void bar_teardown(struct swaybar *bar); 101void bar_teardown(struct swaybar *bar);
102 102
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 6739c28a..5f5688cf 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -50,7 +50,7 @@ struct swaybar_config {
50 } colors; 50 } colors;
51}; 51};
52 52
53struct swaybar_config *init_config(); 53struct swaybar_config *init_config(void);
54void free_config(struct swaybar_config *config); 54void free_config(struct swaybar_config *config);
55uint32_t parse_position(const char *position); 55uint32_t parse_position(const char *position);
56 56
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h
new file mode 100644
index 00000000..12d9b317
--- /dev/null
+++ b/include/swaybar/i3bar.h
@@ -0,0 +1,34 @@
1#ifndef _SWAYBAR_I3BAR_H
2#define _SWAYBAR_I3BAR_H
3
4#include "bar.h"
5#include "status_line.h"
6
7struct i3bar_block {
8 struct wl_list link;
9 int ref_count;
10 char *full_text, *short_text, *align;
11 bool urgent;
12 uint32_t *color;
13 int min_width;
14 char *name, *instance;
15 bool separator;
16 int separator_block_width;
17 bool markup;
18 // Airblader features
19 uint32_t background;
20 uint32_t border;
21 int border_top;
22 int border_bottom;
23 int border_left;
24 int border_right;
25};
26
27void i3bar_block_unref(struct i3bar_block *block);
28bool i3bar_handle_readable(struct status_line *status);
29enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
30 struct i3bar_block *block, int x, int y, enum x11_button button);
31enum x11_button wl_button_to_x11_button(uint32_t button);
32enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value);
33
34#endif
diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h
index a1696bcf..81e48a6b 100644
--- a/include/swaybar/ipc.h
+++ b/include/swaybar/ipc.h
@@ -3,7 +3,7 @@
3#include <stdbool.h> 3#include <stdbool.h>
4#include "swaybar/bar.h" 4#include "swaybar/bar.h"
5 5
6void ipc_initialize(struct swaybar *bar, const char *bar_id); 6bool ipc_initialize(struct swaybar *bar, const char *bar_id);
7bool handle_ipc_readable(struct swaybar *bar); 7bool handle_ipc_readable(struct swaybar *bar);
8void ipc_get_workspaces(struct swaybar *bar); 8void ipc_get_workspaces(struct swaybar *bar);
9void ipc_send_workspace_command(struct swaybar *bar, const char *ws); 9void ipc_send_workspace_command(struct swaybar *bar, const char *ws);
diff --git a/include/swaybar/render.h b/include/swaybar/render.h
index 071e2298..ebdc69e4 100644
--- a/include/swaybar/render.h
+++ b/include/swaybar/render.h
@@ -1,10 +1,8 @@
1#ifndef _SWAYBAR_RENDER_H 1#ifndef _SWAYBAR_RENDER_H
2#define _SWAYBAR_RENDER_H 2#define _SWAYBAR_RENDER_H
3 3
4struct swaybar;
5struct swaybar_output; 4struct swaybar_output;
6struct swaybar_config;
7 5
8void render_frame(struct swaybar *bar, struct swaybar_output *output); 6void render_frame(struct swaybar_output *output);
9 7
10#endif 8#endif
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index d3eabdf6..e6c7dac2 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -13,26 +13,6 @@ enum status_protocol {
13 PROTOCOL_I3BAR, 13 PROTOCOL_I3BAR,
14}; 14};
15 15
16struct i3bar_block {
17 struct wl_list link;
18 int ref_count;
19 char *full_text, *short_text, *align;
20 bool urgent;
21 uint32_t *color;
22 int min_width;
23 char *name, *instance;
24 bool separator;
25 int separator_block_width;
26 bool markup;
27 // Airblader features
28 uint32_t background;
29 uint32_t border;
30 int border_top;
31 int border_bottom;
32 int border_left;
33 int border_right;
34};
35
36struct status_line { 16struct status_line {
37 pid_t pid; 17 pid_t pid;
38 int read_fd, write_fd; 18 int read_fd, write_fd;
@@ -55,11 +35,5 @@ struct status_line *status_line_init(char *cmd);
55void status_error(struct status_line *status, const char *text); 35void status_error(struct status_line *status, const char *text);
56bool status_handle_readable(struct status_line *status); 36bool status_handle_readable(struct status_line *status);
57void status_line_free(struct status_line *status); 37void status_line_free(struct status_line *status);
58bool i3bar_handle_readable(struct status_line *status);
59enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
60 struct i3bar_block *block, int x, int y, enum x11_button button);
61void i3bar_block_unref(struct i3bar_block *block);
62enum x11_button wl_button_to_x11_button(uint32_t button);
63enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value);
64 38
65#endif 39#endif
diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h
deleted file mode 100644
index eb9cfea7..00000000
--- a/include/swaybar/tray/dbus.h
+++ /dev/null
@@ -1,18 +0,0 @@
1#ifndef _SWAYBAR_DBUS_H
2#define _SWAYBAR_DBUS_H
3
4#include <stdbool.h>
5#include <dbus/dbus.h>
6extern DBusConnection *conn;
7
8/**
9 * Should be called in main loop to dispatch events
10 */
11void dispatch_dbus();
12
13/**
14 * Initializes async dbus communication
15 */
16int dbus_init();
17
18#endif /* _SWAYBAR_DBUS_H */
diff --git a/include/swaybar/tray/sni_watcher.h b/include/swaybar/tray/sni_watcher.h
deleted file mode 100644
index 25ddfcd2..00000000
--- a/include/swaybar/tray/sni_watcher.h
+++ /dev/null
@@ -1,10 +0,0 @@
1#ifndef _SWAYBAR_SNI_WATCHER_H
2#define _SWAYBAR_SNI_WATCHER_H
3
4/**
5 * Starts the sni_watcher, the watcher is practically a black box and should
6 * only be accessed though functions described in its spec
7 */
8int init_sni_watcher();
9
10#endif /* _SWAYBAR_SNI_WATCHER_H */
diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h
deleted file mode 100644
index 2d0662be..00000000
--- a/include/swaybar/tray/tray.h
+++ /dev/null
@@ -1,32 +0,0 @@
1#ifndef _SWAYBAR_TRAY_H
2#define _SWAYBAR_TRAY_H
3
4#include <stdint.h>
5#include <stdbool.h>
6#include "swaybar/tray/dbus.h"
7#include "swaybar/tray/sni.h"
8#include "swaybar/bar.h"
9#include "list.h"
10
11extern struct tray *tray;
12
13struct tray {
14 list_t *items;
15};
16
17/**
18 * Processes a mouse event on the bar
19 */
20void tray_mouse_event(struct output *output, int x, int y,
21 uint32_t button, uint32_t state);
22
23uint32_t tray_render(struct output *output, struct config *config);
24
25void tray_upkeep(struct bar *bar);
26
27/**
28 * Initializes the tray with D-Bus
29 */
30void init_tray(struct bar *bar);
31
32#endif /* _SWAYBAR_TRAY_H */
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index 950cfaaf..970e3cc9 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -82,6 +82,7 @@ struct swaylock_surface {
82 bool frame_pending, dirty; 82 bool frame_pending, dirty;
83 uint32_t width, height; 83 uint32_t width, height;
84 int32_t scale; 84 int32_t scale;
85 enum wl_output_subpixel subpixel;
85 char *output_name; 86 char *output_name;
86 struct wl_list link; 87 struct wl_list link;
87}; 88};
@@ -100,5 +101,8 @@ void render_frame(struct swaylock_surface *surface);
100void render_frames(struct swaylock_state *state); 101void render_frames(struct swaylock_state *state);
101void damage_surface(struct swaylock_surface *surface); 102void damage_surface(struct swaylock_surface *surface);
102void damage_state(struct swaylock_state *state); 103void damage_state(struct swaylock_state *state);
104void initialize_pw_backend(void);
105bool attempt_password(struct swaylock_password *pw);
106void clear_password_buffer(struct swaylock_password *pw);
103 107
104#endif 108#endif
diff --git a/meson.build b/meson.build
index 253a4e96..1e7ce281 100644
--- a/meson.build
+++ b/meson.build
@@ -22,6 +22,10 @@ datadir = get_option('datadir')
22sysconfdir = get_option('sysconfdir') 22sysconfdir = get_option('sysconfdir')
23prefix = get_option('prefix') 23prefix = get_option('prefix')
24 24
25if is_freebsd
26 add_project_arguments('-D_C11_SOURCE', language: 'c')
27endif
28
25swayidle_deps = [] 29swayidle_deps = []
26 30
27jsonc = dependency('json-c', version: '>=0.13') 31jsonc = dependency('json-c', version: '>=0.13')
@@ -38,9 +42,8 @@ pango = dependency('pango')
38pangocairo = dependency('pangocairo') 42pangocairo = dependency('pangocairo')
39gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) 43gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
40pixman = dependency('pixman-1') 44pixman = dependency('pixman-1')
41libcap = dependency('libcap', required: false)
42libinput = dependency('libinput', version: '>=1.6.0') 45libinput = dependency('libinput', version: '>=1.6.0')
43libpam = cc.find_library('pam') 46libpam = cc.find_library('pam', required: false)
44systemd = dependency('libsystemd', required: false) 47systemd = dependency('libsystemd', required: false)
45elogind = dependency('libelogind', required: false) 48elogind = dependency('libelogind', required: false)
46math = cc.find_library('m') 49math = cc.find_library('m')
@@ -70,6 +73,11 @@ if elogind.found()
70 swayidle_deps += elogind 73 swayidle_deps += elogind
71endif 74endif
72 75
76if not systemd.found() and not elogind.found()
77 warning('The sway binary must be setuid when compiled without (e)logind')
78 warning('You must do this manually post-install: chmod a+s /path/to/sway')
79endif
80
73scdoc = find_program('scdoc', required: false) 81scdoc = find_program('scdoc', required: false)
74 82
75if scdoc.found() 83if scdoc.found()
@@ -120,7 +128,16 @@ else
120endif 128endif
121add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') 129add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
122 130
123add_project_arguments('-D_LD_LIBRARY_PATH="@0@"'.format(get_option('ld-library-path')), language: 'c') 131if get_option('use_rpath')
132 if get_option('custom_rpath') == ''
133 # default to platform specific libdir, one level up from the binary
134 rpathdir = join_paths('$ORIGIN', '..', '$LIB')
135 else
136 rpathdir = get_option('custom_rpath')
137 endif
138else
139 rpathdir = ''
140endif
124 141
125sway_inc = include_directories('include') 142sway_inc = include_directories('include')
126 143
@@ -133,9 +150,9 @@ subdir('swaymsg')
133subdir('client') 150subdir('client')
134subdir('swaybg') 151subdir('swaybg')
135subdir('swaybar') 152subdir('swaybar')
136subdir('swaylock')
137subdir('swayidle') 153subdir('swayidle')
138subdir('swaynag') 154subdir('swaynag')
155subdir('swaylock')
139 156
140config = configuration_data() 157config = configuration_data()
141config.set('sysconfdir', join_paths(prefix, sysconfdir)) 158config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/meson_options.txt b/meson_options.txt
index 50d646fd..2db852fc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,5 +1,6 @@
1option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.') 1option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.')
2option('ld-library-path', type: 'string', value: '', description: 'The LD_LIBRARY_PATH environment variable.') 2option('use_rpath', type: 'boolean', value: false, description: 'install binaries with rpath set')
3option('custom_rpath', type: 'string', value: '', description: 'override rpath with a custom one')
3option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') 4option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.')
4option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') 5option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
5option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') 6option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
diff --git a/sway/commands.c b/sway/commands.c
index 07169f1e..03761c52 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -55,22 +55,6 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
55 return error; 55 return error;
56} 56}
57 57
58void apply_input_config(struct input_config *input) {
59 int i;
60 i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
61 if (i >= 0) {
62 // merge existing config
63 struct input_config *ic = config->input_configs->items[i];
64 merge_input_config(ic, input);
65 free_input_config(input);
66 input = ic;
67 } else {
68 list_add(config->input_configs, input);
69 }
70
71 input_manager_apply_input_config(input_manager, input);
72}
73
74void apply_seat_config(struct seat_config *seat_config) { 58void apply_seat_config(struct seat_config *seat_config) {
75 int i; 59 int i;
76 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name); 60 i = list_seq_find(config->seat_configs, seat_name_cmp, seat_config->name);
@@ -237,7 +221,8 @@ static void set_config_node(struct sway_node *node) {
237 } 221 }
238} 222}
239 223
240struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { 224struct cmd_results *execute_command(char *_exec, struct sway_seat *seat,
225 struct sway_container *con) {
241 // Even though this function will process multiple commands we will only 226 // Even though this function will process multiple commands we will only
242 // return the last error, if any (for now). (Since we have access to an 227 // return the last error, if any (for now). (Since we have access to an
243 // error string we could e.g. concatenate all errors there.) 228 // error string we could e.g. concatenate all errors there.)
@@ -256,6 +241,15 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
256 } 241 }
257 } 242 }
258 243
244 // This is the container or workspace which this command will run on.
245 // Ignored if the command string contains criteria.
246 struct sway_node *node;
247 if (con) {
248 node = &con->node;
249 } else {
250 node = seat_get_focus_inactive(seat, &root->node);
251 }
252
259 config->handler_context.seat = seat; 253 config->handler_context.seat = seat;
260 254
261 head = exec; 255 head = exec;
@@ -318,9 +312,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
318 } 312 }
319 313
320 if (!config->handler_context.using_criteria) { 314 if (!config->handler_context.using_criteria) {
321 // without criteria, the command acts upon the focused 315 set_config_node(node);
322 // container
323 set_config_node(seat_get_focus_inactive(seat, &root->node));
324 struct cmd_results *res = handler->handle(argc-1, argv+1); 316 struct cmd_results *res = handler->handle(argc-1, argv+1);
325 if (res->status != CMD_SUCCESS) { 317 if (res->status != CMD_SUCCESS) {
326 free_argv(argc, argv); 318 free_argv(argc, argv);
@@ -399,14 +391,12 @@ struct cmd_results *config_command(char *exec) {
399 // Var replacement, for all but first argument of set 391 // Var replacement, for all but first argument of set
400 // TODO commands 392 // TODO commands
401 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 393 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
394 if (*argv[i] == '\"' || *argv[i] == '\'') {
395 strip_quotes(argv[i]);
396 }
402 argv[i] = do_var_replacement(argv[i]); 397 argv[i] = do_var_replacement(argv[i]);
403 unescape_string(argv[i]); 398 unescape_string(argv[i]);
404 } 399 }
405 // Strip quotes for first argument.
406 // TODO This part needs to be handled much better
407 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
408 strip_quotes(argv[1]);
409 }
410 if (handler->handle) { 400 if (handler->handle) {
411 results = handler->handle(argc-1, argv+1); 401 results = handler->handle(argc-1, argv+1);
412 } else { 402 } else {
@@ -430,11 +420,6 @@ struct cmd_results *config_subcommand(char **argv, int argc,
430 char *input = argv[0] ? argv[0] : "(empty)"; 420 char *input = argv[0] ? argv[0] : "(empty)";
431 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); 421 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
432 } 422 }
433 // Strip quotes for first argument.
434 // TODO This part needs to be handled much better
435 if (argc > 1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
436 strip_quotes(argv[1]);
437 }
438 if (handler->handle) { 423 if (handler->handle) {
439 return handler->handle(argc - 1, argv + 1); 424 return handler->handle(argc - 1, argv + 1);
440 } 425 }
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index 0c48bee9..f18b8d7c 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -21,7 +21,9 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
21 config->current_bar->binding_mode_indicator = false; 21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(WLR_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 } else {
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
24 } 27 }
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator", 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26 "Invalid value %s", argv[0]);
27} 29}
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 047018e0..820c2a6a 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -321,7 +321,7 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding)
321 } 321 }
322 322
323 config->handler_context.seat = seat; 323 config->handler_context.seat = seat;
324 struct cmd_results *results = execute_command(binding->command, NULL); 324 struct cmd_results *results = execute_command(binding->command, NULL, NULL);
325 if (results->status == CMD_SUCCESS) { 325 if (results->status == CMD_SUCCESS) {
326 ipc_event_binding(binding_copy); 326 ipc_event_binding(binding_copy);
327 } else { 327 } else {
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index d676e475..2e0876a9 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -1,4 +1,5 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h>
2#include "sway/commands.h" 3#include "sway/commands.h"
3#include "sway/config.h" 4#include "sway/config.h"
4#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
@@ -13,172 +14,173 @@ enum gaps_op {
13 GAPS_OP_SUBTRACT 14 GAPS_OP_SUBTRACT
14}; 15};
15 16
16enum gaps_scope { 17struct gaps_data {
17 GAPS_SCOPE_ALL, 18 bool inner;
18 GAPS_SCOPE_WORKSPACE, 19 enum gaps_op operation;
19 GAPS_SCOPE_CURRENT 20 int amount;
20}; 21};
21 22
22struct cmd_results *cmd_gaps(int argc, char **argv) { 23// gaps edge_gaps on|off|toggle
23 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1); 24static struct cmd_results *gaps_edge_gaps(int argc, char **argv) {
24 if (error) { 25 struct cmd_results *error;
26 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) {
25 return error; 27 return error;
26 } 28 }
27 29
28 if (strcmp(argv[0], "edge_gaps") == 0) { 30 if (strcmp(argv[1], "on") == 0) {
29 if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { 31 config->edge_gaps = true;
30 return error; 32 } else if (strcmp(argv[1], "off") == 0) {
31 } 33 config->edge_gaps = false;
32 34 } else if (strcmp(argv[1], "toggle") == 0) {
33 if (strcmp(argv[1], "on") == 0) { 35 if (!config->active) {
34 config->edge_gaps = true;
35 } else if (strcmp(argv[1], "off") == 0) {
36 config->edge_gaps = false;
37 } else if (strcmp(argv[1], "toggle") == 0) {
38 if (!config->active) {
39 return cmd_results_new(CMD_INVALID, "gaps",
40 "Cannot toggle gaps while not running.");
41 }
42 config->edge_gaps = !config->edge_gaps;
43 } else {
44 return cmd_results_new(CMD_INVALID, "gaps", 36 return cmd_results_new(CMD_INVALID, "gaps",
45 "gaps edge_gaps on|off|toggle"); 37 "Cannot toggle gaps while not running.");
46 } 38 }
47 arrange_root(); 39 config->edge_gaps = !config->edge_gaps;
48 } else { 40 } else {
49 int amount_idx = 0; // the current index in argv 41 return cmd_results_new(CMD_INVALID, "gaps",
50 enum gaps_op op = GAPS_OP_SET; 42 "gaps edge_gaps on|off|toggle");
51 enum gaps_scope scope = GAPS_SCOPE_ALL; 43 }
52 bool inner = true; 44 arrange_root();
53 45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
54 if (strcmp(argv[0], "inner") == 0) { 46}
55 amount_idx++;
56 inner = true;
57 } else if (strcmp(argv[0], "outer") == 0) {
58 amount_idx++;
59 inner = false;
60 }
61 47
62 // If one of the long variants of the gaps command is used 48// gaps inner|outer <px>
63 // (which starts with inner|outer) check the number of args 49static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
64 if (amount_idx > 0) { // if we've seen inner|outer 50 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
65 if (argc > 2) { // check the longest variant 51 if (error) {
66 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 52 return error;
67 if (error) { 53 }
68 return error;
69 }
70 } else { // check the next longest format
71 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
72 if (error) {
73 return error;
74 }
75 }
76 } else {
77 error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 1);
78 if (error) {
79 return error;
80 }
81 }
82 54
83 if (argc == 4) { 55 bool inner;
84 // Long format: all|workspace|current. 56 if (strcasecmp(argv[0], "inner") == 0) {
85 if (strcmp(argv[amount_idx], "all") == 0) { 57 inner = true;
86 amount_idx++; 58 } else if (strcasecmp(argv[0], "outer") == 0) {
87 scope = GAPS_SCOPE_ALL; 59 inner = false;
88 } else if (strcmp(argv[amount_idx], "workspace") == 0) { 60 } else {
89 amount_idx++; 61 return cmd_results_new(CMD_INVALID, "gaps",
90 scope = GAPS_SCOPE_WORKSPACE; 62 "Expected 'gaps inner|outer <px>'");
91 } else if (strcmp(argv[amount_idx], "current") == 0) { 63 }
92 amount_idx++;
93 scope = GAPS_SCOPE_CURRENT;
94 }
95
96 // Long format: set|plus|minus
97 if (strcmp(argv[amount_idx], "set") == 0) {
98 amount_idx++;
99 op = GAPS_OP_SET;
100 } else if (strcmp(argv[amount_idx], "plus") == 0) {
101 amount_idx++;
102 op = GAPS_OP_ADD;
103 } else if (strcmp(argv[amount_idx], "minus") == 0) {
104 amount_idx++;
105 op = GAPS_OP_SUBTRACT;
106 }
107 }
108 64
109 char *end; 65 char *end;
110 double val = strtod(argv[amount_idx], &end); 66 int amount = strtol(argv[1], &end, 10);
111 67 if (strlen(end) && strcasecmp(end, "px") != 0) {
112 if (strlen(end) && val == 0.0) { // invalid <amount> 68 return cmd_results_new(CMD_INVALID, "gaps",
113 // guess which variant of the command was attempted 69 "Expected 'gaps inner|outer <px>'");
114 if (argc == 1) { 70 }
115 return cmd_results_new(CMD_INVALID, "gaps", "gaps <amount>"); 71 if (amount < 0) {
116 } 72 amount = 0;
117 if (argc == 2) { 73 }
118 return cmd_results_new(CMD_INVALID, "gaps",
119 "gaps inner|outer <amount>");
120 }
121 return cmd_results_new(CMD_INVALID, "gaps",
122 "gaps inner|outer all|workspace|current set|plus|minus <amount>");
123 }
124 74
125 if (amount_idx == 0) { // gaps <amount> 75 if (inner) {
126 config->gaps_inner = val; 76 config->gaps_inner = amount;
127 config->gaps_outer = val; 77 } else {
128 arrange_root(); 78 config->gaps_outer = amount;
129 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 79 }
130 } 80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
131 // Other variants. The middle-length variant (gaps inner|outer <amount>) 81}
132 // just defaults the scope to "all" and defaults the op to "set".
133
134 double total;
135 switch (op) {
136 case GAPS_OP_SUBTRACT: {
137 total = (inner ? config->gaps_inner : config->gaps_outer) - val;
138 if (total < 0) {
139 total = 0;
140 }
141 break;
142 }
143 case GAPS_OP_ADD: {
144 total = (inner ? config->gaps_inner : config->gaps_outer) + val;
145 break;
146 }
147 case GAPS_OP_SET: {
148 total = val;
149 break;
150 }
151 }
152 82
153 if (scope == GAPS_SCOPE_ALL) { 83static void configure_gaps(struct sway_workspace *ws, void *_data) {
154 if (inner) { 84 struct gaps_data *data = _data;
155 config->gaps_inner = total; 85 int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer;
156 } else { 86
157 config->gaps_outer = total; 87 switch (data->operation) {
158 } 88 case GAPS_OP_SET:
159 arrange_root(); 89 *prop = data->amount;
160 } else { 90 break;
161 if (scope == GAPS_SCOPE_WORKSPACE) { 91 case GAPS_OP_ADD:
162 struct sway_workspace *ws = config->handler_context.workspace; 92 *prop += data->amount;
163 ws->has_gaps = true; 93 break;
164 if (inner) { 94 case GAPS_OP_SUBTRACT:
165 ws->gaps_inner = total; 95 *prop -= data->amount;
166 } else { 96 break;
167 ws->gaps_outer = total; 97 }
168 } 98 if (*prop < 0) {
169 arrange_workspace(ws); 99 *prop = 0;
170 } else { 100 }
171 struct sway_container *c = config->handler_context.container; 101 arrange_workspace(ws);
172 c->has_gaps = true; 102}
173 if (inner) { 103
174 c->gaps_inner = total; 104// gaps inner|outer current|all set|plus|minus <px>
175 } else { 105static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
176 c->gaps_outer = total; 106 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
177 } 107 if (error) {
178 arrange_workspace(c->workspace); 108 return error;
179 } 109 }
180 } 110
111 struct gaps_data data;
112
113 if (strcasecmp(argv[0], "inner") == 0) {
114 data.inner = true;
115 } else if (strcasecmp(argv[0], "outer") == 0) {
116 data.inner = false;
117 } else {
118 return cmd_results_new(CMD_INVALID, "gaps",
119 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
120 }
121
122 bool all;
123 if (strcasecmp(argv[1], "current") == 0) {
124 all = false;
125 } else if (strcasecmp(argv[1], "all") == 0) {
126 all = true;
127 } else {
128 return cmd_results_new(CMD_INVALID, "gaps",
129 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
130 }
131
132 if (strcasecmp(argv[2], "set") == 0) {
133 data.operation = GAPS_OP_SET;
134 } else if (strcasecmp(argv[2], "plus") == 0) {
135 data.operation = GAPS_OP_ADD;
136 } else if (strcasecmp(argv[2], "minus") == 0) {
137 data.operation = GAPS_OP_SUBTRACT;
138 } else {
139 return cmd_results_new(CMD_INVALID, "gaps",
140 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
141 }
142
143 char *end;
144 data.amount = strtol(argv[3], &end, 10);
145 if (strlen(end) && strcasecmp(end, "px") != 0) {
146 return cmd_results_new(CMD_INVALID, "gaps",
147 "Expected 'gaps inner|outer current|all set|plus|minus <px>'");
148 }
149
150 if (all) {
151 root_for_each_workspace(configure_gaps, &data);
152 } else {
153 configure_gaps(config->handler_context.workspace, &data);
181 } 154 }
182 155
183 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 156 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
184} 157}
158
159// gaps edge_gaps on|off|toggle
160// gaps inner|outer <px> - sets defaults for workspaces
161// gaps inner|outer current|all set|plus|minus <px> - runtime only
162struct cmd_results *cmd_gaps(int argc, char **argv) {
163 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2);
164 if (error) {
165 return error;
166 }
167
168 if (strcmp(argv[0], "edge_gaps") == 0) {
169 return gaps_edge_gaps(argc, argv);
170 }
171
172 if (argc == 2) {
173 return gaps_set_defaults(argc, argv);
174 }
175 if (argc == 4) {
176 if (config->active) {
177 return gaps_set_runtime(argc, argv);
178 } else {
179 return cmd_results_new(CMD_INVALID, "gaps",
180 "This syntax can only be used when sway is running");
181 }
182 }
183 return cmd_results_new(CMD_INVALID, "gaps",
184 "Expected 'gaps inner|outer <px>' or "
185 "'gaps inner|outer current|all set|plus|minus <px>'");
186}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 84888fbb..2889d47d 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -9,6 +9,7 @@
9static struct cmd_handler input_handlers[] = { 9static struct cmd_handler input_handlers[] = {
10 { "accel_profile", input_cmd_accel_profile }, 10 { "accel_profile", input_cmd_accel_profile },
11 { "click_method", input_cmd_click_method }, 11 { "click_method", input_cmd_click_method },
12 { "drag", input_cmd_drag },
12 { "drag_lock", input_cmd_drag_lock }, 13 { "drag_lock", input_cmd_drag_lock },
13 { "dwt", input_cmd_dwt }, 14 { "dwt", input_cmd_dwt },
14 { "events", input_cmd_events }, 15 { "events", input_cmd_events },
@@ -66,7 +67,15 @@ struct cmd_results *cmd_input(int argc, char **argv) {
66 input_handlers, sizeof(input_handlers)); 67 input_handlers, sizeof(input_handlers));
67 } 68 }
68 69
69 free_input_config(config->handler_context.input_config); 70 if (!res || res->status == CMD_SUCCESS) {
71 struct input_config *ic =
72 store_input_config(config->handler_context.input_config);
73
74 input_manager_apply_input_config(input_manager, ic);
75 } else {
76 free_input_config(config->handler_context.input_config);
77 }
78
70 config->handler_context.input_config = NULL; 79 config->handler_context.input_config = NULL;
71 80
72 return res; 81 return res;
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
index a4108ec3..f7016790 100644
--- a/sway/commands/input/accel_profile.c
+++ b/sway/commands/input/accel_profile.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
9 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "accel_profile", 14 return cmd_results_new(CMD_FAILURE, "accel_profile",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "adaptive") == 0) { 18 if (strcasecmp(argv[0], "adaptive") == 0) {
22 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; 19 ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
23 } else if (strcasecmp(argv[0], "flat") == 0) { 20 } else if (strcasecmp(argv[0], "flat") == 0) {
24 new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; 21 ic->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "accel_profile", 23 return cmd_results_new(CMD_INVALID, "accel_profile",
28 "Expected 'accel_profile <adaptive|flat>'"); 24 "Expected 'accel_profile <adaptive|flat>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
index 5d0d8cc2..4d7e1c93 100644
--- a/sway/commands/input/click_method.c
+++ b/sway/commands/input/click_method.c
@@ -10,27 +10,22 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "click_method", 15 return cmd_results_new(CMD_FAILURE, "click_method",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (strcasecmp(argv[0], "none") == 0) { 19 if (strcasecmp(argv[0], "none") == 0) {
23 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; 20 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
24 } else if (strcasecmp(argv[0], "button_areas") == 0) { 21 } else if (strcasecmp(argv[0], "button_areas") == 0) {
25 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; 22 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
26 } else if (strcasecmp(argv[0], "clickfinger") == 0) { 23 } else if (strcasecmp(argv[0], "clickfinger") == 0) {
27 new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; 24 ic->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
28 } else { 25 } else {
29 free_input_config(new_config);
30 return cmd_results_new(CMD_INVALID, "click_method", 26 return cmd_results_new(CMD_INVALID, "click_method",
31 "Expected 'click_method <none|button_areas|clickfinger'"); 27 "Expected 'click_method <none|button_areas|clickfinger'");
32 } 28 }
33 29
34 apply_input_config(new_config);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 30 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36} 31}
diff --git a/sway/commands/input/drag.c b/sway/commands/input/drag.c
new file mode 100644
index 00000000..e325df29
--- /dev/null
+++ b/sway/commands/input/drag.c
@@ -0,0 +1,26 @@
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#include "util.h"
7
8struct cmd_results *input_cmd_drag(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "drag", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13 struct input_config *ic = config->handler_context.input_config;
14 if (!ic) {
15 return cmd_results_new(CMD_FAILURE,
16 "drag", "No input device defined.");
17 }
18
19 if (parse_boolean(argv[0], true)) {
20 ic->drag = LIBINPUT_CONFIG_DRAG_ENABLED;
21 } else {
22 ic->drag = LIBINPUT_CONFIG_DRAG_DISABLED;
23 }
24
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26}
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index f9ddeef2..db5d5afa 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -10,21 +10,17 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
10 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, 15 return cmd_results_new(CMD_FAILURE,
17 "drag_lock", "No input device defined."); 16 "drag_lock", "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; 20 ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
24 } else { 21 } else {
25 new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; 22 ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
26 } 23 }
27 24
28 apply_input_config(new_config);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30} 26}
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 15134268..0c3881dd 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -10,20 +10,16 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
10 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined."); 15 return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (parse_boolean(argv[0], true)) { 18 if (parse_boolean(argv[0], true)) {
22 new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; 19 ic->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
23 } else { 20 } else {
24 new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; 21 ic->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
25 } 22 }
26 23
27 apply_input_config(new_config);
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 25}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index abfe3b12..e7ed69c6 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -10,30 +10,23 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "events", 15 return cmd_results_new(CMD_FAILURE, "events",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 wlr_log(WLR_DEBUG, "events for device: %s",
20 current_input_config->identifier);
21 struct input_config *new_config =
22 new_input_config(current_input_config->identifier);
23 18
24 if (strcasecmp(argv[0], "enabled") == 0) { 19 if (strcasecmp(argv[0], "enabled") == 0) {
25 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; 20 ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
26 } else if (strcasecmp(argv[0], "disabled") == 0) { 21 } else if (strcasecmp(argv[0], "disabled") == 0) {
27 new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; 22 ic->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
28 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 23 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
29 new_config->send_events = 24 ic->send_events =
30 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 25 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
31 } else { 26 } else {
32 free_input_config(new_config);
33 return cmd_results_new(CMD_INVALID, "events", 27 return cmd_results_new(CMD_INVALID, "events",
34 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 28 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
35 } 29 }
36 30
37 apply_input_config(new_config);
38 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
39} 32}
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index e770043a..2e0f757b 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -10,17 +10,13 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
10 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "left_handed", 15 return cmd_results_new(CMD_FAILURE, "left_handed",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 new_config->left_handed = parse_boolean(argv[0], true); 19 ic->left_handed = parse_boolean(argv[0], true);
23 20
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 22}
diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c
index 40f04214..53608a67 100644
--- a/sway/commands/input/map_from_region.c
+++ b/sway/commands/input/map_from_region.c
@@ -38,50 +38,44 @@ struct cmd_results *input_cmd_map_from_region(int argc, char **argv) {
38 if ((error = checkarg(argc, "map_from_region", EXPECTED_EQUAL_TO, 2))) { 38 if ((error = checkarg(argc, "map_from_region", EXPECTED_EQUAL_TO, 2))) {
39 return error; 39 return error;
40 } 40 }
41 struct input_config *current_input_config = 41 struct input_config *ic = config->handler_context.input_config;
42 config->handler_context.input_config; 42 if (!ic) {
43 if (!current_input_config) {
44 return cmd_results_new(CMD_FAILURE, "map_from_region", 43 return cmd_results_new(CMD_FAILURE, "map_from_region",
45 "No input device defined"); 44 "No input device defined");
46 } 45 }
47 46
48 struct input_config *new_config = 47 ic->mapped_from_region =
49 new_input_config(current_input_config->identifier);
50
51 new_config->mapped_from_region =
52 calloc(1, sizeof(struct input_config_mapped_from_region)); 48 calloc(1, sizeof(struct input_config_mapped_from_region));
53 49
54 bool mm1, mm2; 50 bool mm1, mm2;
55 if (!parse_coords(argv[0], &new_config->mapped_from_region->x1, 51 if (!parse_coords(argv[0], &ic->mapped_from_region->x1,
56 &new_config->mapped_from_region->y1, &mm1)) { 52 &ic->mapped_from_region->y1, &mm1)) {
57 free(new_config->mapped_from_region); 53 free(ic->mapped_from_region);
58 free_input_config(new_config); 54 ic->mapped_from_region = NULL;
59 return cmd_results_new(CMD_FAILURE, "map_from_region", 55 return cmd_results_new(CMD_FAILURE, "map_from_region",
60 "Invalid top-left coordinates"); 56 "Invalid top-left coordinates");
61 } 57 }
62 if (!parse_coords(argv[1], &new_config->mapped_from_region->x2, 58 if (!parse_coords(argv[1], &ic->mapped_from_region->x2,
63 &new_config->mapped_from_region->y2, &mm2)) { 59 &ic->mapped_from_region->y2, &mm2)) {
64 free(new_config->mapped_from_region); 60 free(ic->mapped_from_region);
65 free_input_config(new_config); 61 ic->mapped_from_region = NULL;
66 return cmd_results_new(CMD_FAILURE, "map_from_region", 62 return cmd_results_new(CMD_FAILURE, "map_from_region",
67 "Invalid bottom-right coordinates"); 63 "Invalid bottom-right coordinates");
68 } 64 }
69 if (new_config->mapped_from_region->x1 > new_config->mapped_from_region->x2 || 65 if (ic->mapped_from_region->x1 > ic->mapped_from_region->x2 ||
70 new_config->mapped_from_region->y1 > new_config->mapped_from_region->y2) { 66 ic->mapped_from_region->y1 > ic->mapped_from_region->y2) {
71 free(new_config->mapped_from_region); 67 free(ic->mapped_from_region);
72 free_input_config(new_config); 68 ic->mapped_from_region = NULL;
73 return cmd_results_new(CMD_FAILURE, "map_from_region", 69 return cmd_results_new(CMD_FAILURE, "map_from_region",
74 "Invalid rectangle"); 70 "Invalid rectangle");
75 } 71 }
76 if (mm1 != mm2) { 72 if (mm1 != mm2) {
77 free(new_config->mapped_from_region); 73 free(ic->mapped_from_region);
78 free_input_config(new_config); 74 ic->mapped_from_region = NULL;
79 return cmd_results_new(CMD_FAILURE, "map_from_region", 75 return cmd_results_new(CMD_FAILURE, "map_from_region",
80 "Both coordinates must be in the same unit"); 76 "Both coordinates must be in the same unit");
81 } 77 }
82 new_config->mapped_from_region->mm = mm1; 78 ic->mapped_from_region->mm = mm1;
83
84 apply_input_config(new_config);
85 79
86 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 80 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
87} 81}
diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c
index 68439bec..8b16c557 100644
--- a/sway/commands/input/map_to_output.c
+++ b/sway/commands/input/map_to_output.c
@@ -11,17 +11,13 @@ struct cmd_results *input_cmd_map_to_output(int argc, char **argv) {
11 if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) { 11 if ((error = checkarg(argc, "map_to_output", EXPECTED_EQUAL_TO, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct input_config *current_input_config = 14 struct input_config *ic = config->handler_context.input_config;
15 config->handler_context.input_config; 15 if (!ic) {
16 if (!current_input_config) {
17 return cmd_results_new(CMD_FAILURE, "map_to_output", 16 return cmd_results_new(CMD_FAILURE, "map_to_output",
18 "No input device defined."); 17 "No input device defined.");
19 } 18 }
20 struct input_config *new_config =
21 new_input_config(current_input_config->identifier);
22 19
23 new_config->mapped_to_output = strdup(argv[0]); 20 ic->mapped_to_output = strdup(argv[0]);
24 apply_input_config(new_config);
25 21
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 23}
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index 414d4d2b..80d26838 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -10,22 +10,17 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
10 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "middle_emulation", 15 return cmd_results_new(CMD_FAILURE, "middle_emulation",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; 20 ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
24 } else { 21 } else {
25 new_config->middle_emulation = 22 ic->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
26 LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
27 } 23 }
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 77c3ff00..e2a93500 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -10,17 +10,13 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
10 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "natural_scoll", 15 return cmd_results_new(CMD_FAILURE, "natural_scoll",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 new_config->natural_scroll = parse_boolean(argv[0], true); 19 ic->natural_scroll = parse_boolean(argv[0], true);
23 20
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 22}
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index 8bbd0724..df487b1c 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
9 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "pointer_accel", "No input device defined."); 15 "pointer_accel", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 float pointer_accel = atof(argv[0]); 18 float pointer_accel = atof(argv[0]);
22 if (pointer_accel < -1 || pointer_accel > 1) { 19 if (pointer_accel < -1 || pointer_accel > 1) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "pointer_accel", 20 return cmd_results_new(CMD_INVALID, "pointer_accel",
25 "Input out of range [-1, 1]"); 21 "Input out of range [-1, 1]");
26 } 22 }
27 new_config->pointer_accel = pointer_accel; 23 ic->pointer_accel = pointer_accel;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/repeat_delay.c b/sway/commands/input/repeat_delay.c
index c9ddbf0e..d94b3e4d 100644
--- a/sway/commands/input/repeat_delay.c
+++ b/sway/commands/input/repeat_delay.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_delay(int argc, char **argv) {
9 if ((error = checkarg(argc, "repeat_delay", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "repeat_delay", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "repeat_delay", "No input device defined."); 15 "repeat_delay", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 int repeat_delay = atoi(argv[0]); 18 int repeat_delay = atoi(argv[0]);
22 if (repeat_delay < 0) { 19 if (repeat_delay < 0) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "repeat_delay", 20 return cmd_results_new(CMD_INVALID, "repeat_delay",
25 "Repeat delay cannot be negative"); 21 "Repeat delay cannot be negative");
26 } 22 }
27 new_config->repeat_delay = repeat_delay; 23 ic->repeat_delay = repeat_delay;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/repeat_rate.c b/sway/commands/input/repeat_rate.c
index 56878176..ebec4cdb 100644
--- a/sway/commands/input/repeat_rate.c
+++ b/sway/commands/input/repeat_rate.c
@@ -9,23 +9,18 @@ struct cmd_results *input_cmd_repeat_rate(int argc, char **argv) {
9 if ((error = checkarg(argc, "repeat_rate", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "repeat_rate", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
16 "repeat_rate", "No input device defined."); 15 "repeat_rate", "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 int repeat_rate = atoi(argv[0]); 18 int repeat_rate = atoi(argv[0]);
22 if (repeat_rate < 0) { 19 if (repeat_rate < 0) {
23 free_input_config(new_config);
24 return cmd_results_new(CMD_INVALID, "repeat_rate", 20 return cmd_results_new(CMD_INVALID, "repeat_rate",
25 "Repeat rate cannot be negative"); 21 "Repeat rate cannot be negative");
26 } 22 }
27 new_config->repeat_rate = repeat_rate; 23 ic->repeat_rate = repeat_rate;
28 24
29 apply_input_config(new_config);
30 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
31} 26}
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 350fcca2..1958f23c 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -10,35 +10,28 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
10 if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) { 10 if ((error = checkarg(argc, "scroll_button", EXPECTED_AT_LEAST, 1))) {
11 return error; 11 return error;
12 } 12 }
13 struct input_config *current_input_config = 13 struct input_config *ic = config->handler_context.input_config;
14 config->handler_context.input_config; 14 if (!ic) {
15 if (!current_input_config) {
16 return cmd_results_new(CMD_FAILURE, "scroll_button", 15 return cmd_results_new(CMD_FAILURE, "scroll_button",
17 "No input device defined."); 16 "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 errno = 0; 19 errno = 0;
23 char *endptr; 20 char *endptr;
24 int scroll_button = strtol(*argv, &endptr, 10); 21 int scroll_button = strtol(*argv, &endptr, 10);
25 if (endptr == *argv && scroll_button == 0) { 22 if (endptr == *argv && scroll_button == 0) {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "scroll_button", 23 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier must be an integer."); 24 "Scroll button identifier must be an integer.");
29 } 25 }
30 if (errno == ERANGE) { 26 if (errno == ERANGE) {
31 free_input_config(new_config);
32 return cmd_results_new(CMD_INVALID, "scroll_button", 27 return cmd_results_new(CMD_INVALID, "scroll_button",
33 "Scroll button identifier out of range."); 28 "Scroll button identifier out of range.");
34 } 29 }
35 if (scroll_button < 0) { 30 if (scroll_button < 0) {
36 free_input_config(new_config);
37 return cmd_results_new(CMD_INVALID, "scroll_button", 31 return cmd_results_new(CMD_INVALID, "scroll_button",
38 "Scroll button identifier cannot be negative."); 32 "Scroll button identifier cannot be negative.");
39 } 33 }
40 new_config->scroll_button = scroll_button; 34 ic->scroll_button = scroll_button;
41 35
42 apply_input_config(new_config);
43 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
44} 37}
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
index 4c6ac6b6..c116b052 100644
--- a/sway/commands/input/scroll_method.c
+++ b/sway/commands/input/scroll_method.c
@@ -9,29 +9,24 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
9 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "scroll_method", 14 return cmd_results_new(CMD_FAILURE, "scroll_method",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "none") == 0) { 18 if (strcasecmp(argv[0], "none") == 0) {
22 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; 19 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
23 } else if (strcasecmp(argv[0], "two_finger") == 0) { 20 } else if (strcasecmp(argv[0], "two_finger") == 0) {
24 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; 21 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
25 } else if (strcasecmp(argv[0], "edge") == 0) { 22 } else if (strcasecmp(argv[0], "edge") == 0) {
26 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; 23 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
27 } else if (strcasecmp(argv[0], "on_button_down") == 0) { 24 } else if (strcasecmp(argv[0], "on_button_down") == 0) {
28 new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; 25 ic->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
29 } else { 26 } else {
30 free_input_config(new_config);
31 return cmd_results_new(CMD_INVALID, "scroll_method", 27 return cmd_results_new(CMD_INVALID, "scroll_method",
32 "Expected 'scroll_method <none|two_finger|edge|on_button_down>'"); 28 "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
33 } 29 }
34 30
35 apply_input_config(new_config);
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37} 32}
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index ac3b8237..c455b696 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -11,22 +11,16 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
11 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) { 11 if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct input_config *current_input_config = 14 struct input_config *ic = config->handler_context.input_config;
15 config->handler_context.input_config; 15 if (!ic) {
16 if (!current_input_config) {
17 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined."); 16 return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
18 } 17 }
19 struct input_config *new_config =
20 new_input_config(current_input_config->identifier);
21 18
22 if (parse_boolean(argv[0], true)) { 19 if (parse_boolean(argv[0], true)) {
23 new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; 20 ic->tap = LIBINPUT_CONFIG_TAP_ENABLED;
24 } else { 21 } else {
25 new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; 22 ic->tap = LIBINPUT_CONFIG_TAP_DISABLED;
26 } 23 }
27 24
28 wlr_log(WLR_DEBUG, "apply-tap for device: %s",
29 current_input_config->identifier);
30 apply_input_config(new_config);
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32} 26}
diff --git a/sway/commands/input/tap_button_map.c b/sway/commands/input/tap_button_map.c
index bdbba472..dff2985b 100644
--- a/sway/commands/input/tap_button_map.c
+++ b/sway/commands/input/tap_button_map.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_tap_button_map(int argc, char **argv) {
9 if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "tap_button_map", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "tap_button_map", 14 return cmd_results_new(CMD_FAILURE, "tap_button_map",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "lrm") == 0) { 18 if (strcasecmp(argv[0], "lrm") == 0) {
22 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; 19 ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
23 } else if (strcasecmp(argv[0], "lmr") == 0) { 20 } else if (strcasecmp(argv[0], "lmr") == 0) {
24 new_config->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; 21 ic->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "tap_button_map", 23 return cmd_results_new(CMD_INVALID, "tap_button_map",
28 "Expected 'tap_button_map <lrm|lmr>'"); 24 "Expected 'tap_button_map <lrm|lmr>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c
index 5442c463..669b4ea9 100644
--- a/sway/commands/input/xkb_capslock.c
+++ b/sway/commands/input/xkb_capslock.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "xkb_capslock", 14 return cmd_results_new(CMD_FAILURE, "xkb_capslock",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "enabled") == 0) { 18 if (strcasecmp(argv[0], "enabled") == 0) {
22 new_config->xkb_capslock = 1; 19 ic->xkb_capslock = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) { 20 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->xkb_capslock = 0; 21 ic->xkb_capslock = 0;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "xkb_capslock", 23 return cmd_results_new(CMD_INVALID, "xkb_capslock",
28 "Expected 'xkb_capslock <enabled|disabled>'"); 24 "Expected 'xkb_capslock <enabled|disabled>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 9fa5a344..5fccd4a3 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_layout",
15 return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_layout = strdup(argv[0]); 18 ic->xkb_layout = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_layout for device: %s layout: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_layout for config: %s layout: %s",
23 current_input_config->identifier, new_config->xkb_layout); 21 ic->identifier, ic->xkb_layout);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index 0d082625..c4d04638 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_model",
15 return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_model = strdup(argv[0]); 18 ic->xkb_model = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_model for device: %s model: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_model for config: %s model: %s",
23 current_input_config->identifier, new_config->xkb_model); 21 ic->identifier, ic->xkb_model);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 39675366..1367da44 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -9,25 +9,20 @@ struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) { 9 if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) {
15 return cmd_results_new(CMD_FAILURE, "xkb_numlock", 14 return cmd_results_new(CMD_FAILURE, "xkb_numlock",
16 "No input device defined."); 15 "No input device defined.");
17 } 16 }
18 struct input_config *new_config =
19 new_input_config(current_input_config->identifier);
20 17
21 if (strcasecmp(argv[0], "enabled") == 0) { 18 if (strcasecmp(argv[0], "enabled") == 0) {
22 new_config->xkb_numlock = 1; 19 ic->xkb_numlock = 1;
23 } else if (strcasecmp(argv[0], "disabled") == 0) { 20 } else if (strcasecmp(argv[0], "disabled") == 0) {
24 new_config->xkb_numlock = 0; 21 ic->xkb_numlock = 0;
25 } else { 22 } else {
26 free_input_config(new_config);
27 return cmd_results_new(CMD_INVALID, "xkb_numlock", 23 return cmd_results_new(CMD_INVALID, "xkb_numlock",
28 "Expected 'xkb_numlock <enabled|disabled>'"); 24 "Expected 'xkb_numlock <enabled|disabled>'");
29 } 25 }
30 26
31 apply_input_config(new_config);
32 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
33} 28}
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 3059d941..794ab6e9 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_options",
15 return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_options = strdup(argv[0]); 18 ic->xkb_options = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_options for device: %s options: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_options for config: %s options: %s",
23 current_input_config->identifier, new_config->xkb_options); 21 ic->identifier, ic->xkb_options);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 560f088e..257c3288 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_rules",
15 return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_rules = strdup(argv[0]); 18 ic->xkb_rules = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_rules for device: %s rules: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_rules for config: %s rules: %s",
23 current_input_config->identifier, new_config->xkb_rules); 21 ic->identifier, ic->xkb_rules);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 0aa03440..3832dc8e 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -9,18 +9,15 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
9 if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) { 9 if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) {
10 return error; 10 return error;
11 } 11 }
12 struct input_config *current_input_config = 12 struct input_config *ic = config->handler_context.input_config;
13 config->handler_context.input_config; 13 if (!ic) {
14 if (!current_input_config) { 14 return cmd_results_new(CMD_FAILURE, "xkb_variant",
15 return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined."); 15 "No input device defined.");
16 } 16 }
17 struct input_config *new_config =
18 new_input_config(current_input_config->identifier);
19 17
20 new_config->xkb_variant = strdup(argv[0]); 18 ic->xkb_variant = strdup(argv[0]);
21 19
22 wlr_log(WLR_DEBUG, "apply-xkb_variant for device: %s variant: %s", 20 wlr_log(WLR_DEBUG, "set-xkb_variant for config: %s variant: %s",
23 current_input_config->identifier, new_config->xkb_variant); 21 ic->identifier, ic->xkb_variant);
24 apply_input_config(new_config);
25 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26} 23}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index ef3ec1cb..c2ce2e78 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -138,15 +138,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
138 } 138 }
139 container->layout = new_layout; 139 container->layout = new_layout;
140 container_update_representation(container); 140 container_update_representation(container);
141 arrange_container(container);
142 } else { 141 } else {
143 if (old_layout != L_TABBED && old_layout != L_STACKED) { 142 if (old_layout != L_TABBED && old_layout != L_STACKED) {
144 workspace->prev_split_layout = old_layout; 143 workspace->prev_split_layout = old_layout;
145 } 144 }
146 workspace->layout = new_layout; 145 workspace->layout = new_layout;
147 workspace_update_representation(workspace); 146 workspace_update_representation(workspace);
148 arrange_workspace(workspace);
149 } 147 }
148 arrange_workspace(workspace);
150 } 149 }
151 150
152 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 151 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 849a18ad..fc2f1cc1 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -259,50 +259,24 @@ static void container_move_to_container(struct sway_container *container,
259 * In other words, rejigger it. */ 259 * In other words, rejigger it. */
260static void workspace_rejigger(struct sway_workspace *ws, 260static void workspace_rejigger(struct sway_workspace *ws,
261 struct sway_container *child, enum movement_direction move_dir) { 261 struct sway_container *child, enum movement_direction move_dir) {
262 if (!child->parent && ws->tiling->length == 1) {
263 ws->layout =
264 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
265 workspace_update_representation(ws);
266 return;
267 }
262 container_detach(child); 268 container_detach(child);
263 workspace_wrap_children(ws); 269 struct sway_container *new_parent = workspace_wrap_children(ws);
264 270
265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1; 271 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
266 workspace_insert_tiling(ws, child, index); 272 workspace_insert_tiling(ws, child, index);
273 container_flatten(new_parent);
267 ws->layout = 274 ws->layout =
268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 275 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
269 workspace_update_representation(ws); 276 workspace_update_representation(ws);
270 child->width = child->height = 0; 277 child->width = child->height = 0;
271} 278}
272 279
273static void move_out_of_tabs_stacks(struct sway_container *container,
274 struct sway_container *current, enum movement_direction move_dir,
275 int offs) {
276 enum sway_container_layout layout = move_dir ==
277 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
278 list_t *siblings = container_get_siblings(container);
279 if (container == current && siblings->length == 1) {
280 wlr_log(WLR_DEBUG, "Changing layout of parent");
281 if (container->parent) {
282 container->parent->layout = layout;
283 container_update_representation(container);
284 } else {
285 container->workspace->layout = layout;
286 workspace_update_representation(container->workspace);
287 }
288 return;
289 }
290
291 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
292 if (current->parent) {
293 struct sway_container *new_parent =
294 container_split(current->parent, layout);
295 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
296 container_reap_empty(new_parent);
297 container_flatten(new_parent);
298 } else {
299 // Changing a workspace
300 struct sway_workspace *workspace = container->workspace;
301 workspace_split(workspace, layout);
302 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
303 }
304}
305
306// Returns true if moved 280// Returns true if moved
307static bool container_move_in_direction(struct sway_container *container, 281static bool container_move_in_direction(struct sway_container *container,
308 enum movement_direction move_dir) { 282 enum movement_direction move_dir) {
@@ -334,7 +308,6 @@ static bool container_move_in_direction(struct sway_container *container,
334 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; 308 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
335 309
336 while (current) { 310 while (current) {
337 struct sway_container *parent = current->parent;
338 list_t *siblings = container_get_siblings(current); 311 list_t *siblings = container_get_siblings(current);
339 enum sway_container_layout layout = container_parent_layout(current); 312 enum sway_container_layout layout = container_parent_layout(current);
340 int index = list_find(siblings, current); 313 int index = list_find(siblings, current);
@@ -343,15 +316,8 @@ static bool container_move_in_direction(struct sway_container *container,
343 if (is_parallel(layout, move_dir)) { 316 if (is_parallel(layout, move_dir)) {
344 if (desired == -1 || desired == siblings->length) { 317 if (desired == -1 || desired == siblings->length) {
345 if (current->parent == container->parent) { 318 if (current->parent == container->parent) {
346 if (!(parent && parent->is_fullscreen) && 319 current = current->parent;
347 (layout == L_TABBED || layout == L_STACKED)) { 320 continue;
348 move_out_of_tabs_stacks(container, current,
349 move_dir, offs);
350 return true;
351 } else {
352 current = current->parent;
353 continue;
354 }
355 } else { 321 } else {
356 // Special case 322 // Special case
357 if (current->parent) { 323 if (current->parent) {
@@ -369,10 +335,6 @@ static bool container_move_in_direction(struct sway_container *container,
369 siblings->items[desired], move_dir); 335 siblings->items[desired], move_dir);
370 return true; 336 return true;
371 } 337 }
372 } else if (!(parent && parent->is_fullscreen) &&
373 (layout == L_TABBED || layout == L_STACKED)) {
374 move_out_of_tabs_stacks(container, current, move_dir, offs);
375 return true;
376 } 338 }
377 339
378 current = current->parent; 340 current = current->parent;
@@ -388,10 +350,8 @@ static bool container_move_in_direction(struct sway_container *container,
388 // Maybe rejigger the workspace 350 // Maybe rejigger the workspace
389 struct sway_workspace *ws = container->workspace; 351 struct sway_workspace *ws = container->workspace;
390 if (!is_parallel(ws->layout, move_dir)) { 352 if (!is_parallel(ws->layout, move_dir)) {
391 if (ws->tiling->length >= 2) { 353 workspace_rejigger(ws, container, move_dir);
392 workspace_rejigger(ws, container, move_dir); 354 return true;
393 return true;
394 }
395 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 355 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
396 workspace_rejigger(ws, container, move_dir); 356 workspace_rejigger(ws, container, move_dir);
397 return true; 357 return true;
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 9e370d43..30fb47c4 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -123,19 +123,13 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 } 123 }
124 free(src); 124 free(src);
125 } else { 125 } else {
126 // Escape spaces and quotes in the final path for swaybg 126 // Escape double quotes in the final path for swaybg
127 for (size_t i = 0; i < strlen(src); i++) { 127 for (size_t i = 0; i < strlen(src); i++) {
128 switch (src[i]) { 128 if (src[i] == '"') {
129 case ' ': 129 src = realloc(src, strlen(src) + 2);
130 case '\'': 130 memmove(src + i + 1, src + i, strlen(src + i) + 1);
131 case '\"': 131 *(src + i) = '\\';
132 src = realloc(src, strlen(src) + 2); 132 i++;
133 memmove(src + i + 1, src + i, strlen(src + i) + 1);
134 *(src + i) = '\\';
135 i++;
136 break;
137 default:
138 break;
139 } 133 }
140 } 134 }
141 135
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 99e9dbda..1343b165 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -179,11 +179,11 @@ static void container_recursive_resize(struct sway_container *container,
179 } 179 }
180} 180}
181 181
182static void resize_tiled(struct sway_container *parent, int amount, 182static bool resize_tiled(struct sway_container *parent, int amount,
183 enum resize_axis axis) { 183 enum resize_axis axis) {
184 struct sway_container *focused = parent; 184 struct sway_container *focused = parent;
185 if (!parent) { 185 if (!parent) {
186 return; 186 return false;
187 } 187 }
188 188
189 enum sway_container_layout parallel_layout = 189 enum sway_container_layout parallel_layout =
@@ -216,7 +216,7 @@ static void resize_tiled(struct sway_container *parent, int amount,
216 } 216 }
217 if (!parent) { 217 if (!parent) {
218 // Can't resize in this direction 218 // Can't resize in this direction
219 return; 219 return false;
220 } 220 }
221 221
222 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
@@ -248,22 +248,22 @@ static void resize_tiled(struct sway_container *parent, int amount,
248 if (sibling_pos < parent_pos && minor_weight) { 248 if (sibling_pos < parent_pos && minor_weight) {
249 double pixels = -amount / minor_weight; 249 double pixels = -amount / minor_weight;
250 if (major_weight && (sibling_size + pixels / 2) < min_sane) { 250 if (major_weight && (sibling_size + pixels / 2) < min_sane) {
251 return; // Too small 251 return false; // Too small
252 } else if (!major_weight && sibling_size + pixels < min_sane) { 252 } else if (!major_weight && sibling_size + pixels < min_sane) {
253 return; // Too small 253 return false; // Too small
254 } 254 }
255 } else if (sibling_pos > parent_pos && major_weight) { 255 } else if (sibling_pos > parent_pos && major_weight) {
256 double pixels = -amount / major_weight; 256 double pixels = -amount / major_weight;
257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) { 257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
258 return; // Too small 258 return false; // Too small
259 } else if (!minor_weight && sibling_size + pixels < min_sane) { 259 } else if (!minor_weight && sibling_size + pixels < min_sane) {
260 return; // Too small 260 return false; // Too small
261 } 261 }
262 } 262 }
263 } else { 263 } else {
264 double pixels = amount; 264 double pixels = amount;
265 if (parent_size + pixels < min_sane) { 265 if (parent_size + pixels < min_sane) {
266 return; // Too small 266 return false; // Too small
267 } 267 }
268 } 268 }
269 } 269 }
@@ -317,9 +317,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
317 } else { 317 } else {
318 arrange_workspace(parent->workspace); 318 arrange_workspace(parent->workspace);
319 } 319 }
320 return true;
320} 321}
321 322
322void container_resize_tiled(struct sway_container *parent, 323bool container_resize_tiled(struct sway_container *parent,
323 enum wlr_edges edge, int amount) { 324 enum wlr_edges edge, int amount) {
324 enum resize_axis axis = RESIZE_AXIS_INVALID; 325 enum resize_axis axis = RESIZE_AXIS_INVALID;
325 switch (edge) { 326 switch (edge) {
@@ -338,7 +339,7 @@ void container_resize_tiled(struct sway_container *parent,
338 case WLR_EDGE_NONE: 339 case WLR_EDGE_NONE:
339 break; 340 break;
340 } 341 }
341 resize_tiled(parent, amount, axis); 342 return resize_tiled(parent, amount, axis);
342} 343}
343 344
344/** 345/**
@@ -395,6 +396,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
395 case RESIZE_AXIS_INVALID: 396 case RESIZE_AXIS_INVALID:
396 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); 397 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
397 } 398 }
399 if (grow_x == 0 && grow_y == 0) {
400 return cmd_results_new(CMD_INVALID, "resize",
401 "Cannot resize any further");
402 }
398 con->x += grow_x; 403 con->x += grow_x;
399 con->y += grow_y; 404 con->y += grow_y;
400 con->width += grow_width; 405 con->width += grow_width;
@@ -442,7 +447,10 @@ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
442 } 447 }
443 } 448 }
444 449
445 resize_tiled(current, amount->amount, axis); 450 if (!resize_tiled(current, amount->amount, axis)) {
451 return cmd_results_new(CMD_INVALID, "resize",
452 "Cannot resize any further");
453 }
446 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 454 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
447} 455}
448 456
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index f026a39d..63f29641 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -10,6 +10,28 @@
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
13static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
14 struct workspace_config *wsc = workspace_find_config(ws_name);
15 if (wsc) {
16 return wsc;
17 }
18 wsc = calloc(1, sizeof(struct workspace_config));
19 if (!wsc) {
20 return NULL;
21 }
22 wsc->workspace = strdup(ws_name);
23 wsc->gaps_inner = -1;
24 wsc->gaps_outer = -1;
25 list_add(config->workspace_configs, wsc);
26 return wsc;
27}
28
29void free_workspace_config(struct workspace_config *wsc) {
30 free(wsc->workspace);
31 free(wsc->output);
32 free(wsc);
33}
34
13struct cmd_results *cmd_workspace(int argc, char **argv) { 35struct cmd_results *cmd_workspace(int argc, char **argv) {
14 struct cmd_results *error = NULL; 36 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { 37 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
@@ -17,6 +39,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
17 } 39 }
18 40
19 int output_location = -1; 41 int output_location = -1;
42 int gaps_location = -1;
20 43
21 for (int i = 0; i < argc; ++i) { 44 for (int i = 0; i < argc; ++i) {
22 if (strcasecmp(argv[i], "output") == 0) { 45 if (strcasecmp(argv[i], "output") == 0) {
@@ -24,25 +47,54 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
24 break; 47 break;
25 } 48 }
26 } 49 }
50 for (int i = 0; i < argc; ++i) {
51 if (strcasecmp(argv[i], "gaps") == 0) {
52 gaps_location = i;
53 break;
54 }
55 }
27 if (output_location >= 0) { 56 if (output_location >= 0) {
28 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { 57 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) {
29 return error; 58 return error;
30 } 59 }
31 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); 60 char *ws_name = join_args(argv, argc - 2);
32 if (!wso) { 61 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
62 free(ws_name);
63 if (!wsc) {
33 return cmd_results_new(CMD_FAILURE, "workspace output", 64 return cmd_results_new(CMD_FAILURE, "workspace output",
34 "Unable to allocate workspace output"); 65 "Unable to allocate workspace output");
35 } 66 }
36 wso->workspace = join_args(argv, argc - 2); 67 free(wsc->output);
37 wso->output = strdup(argv[output_location + 1]); 68 wsc->output = strdup(argv[output_location + 1]);
38 int i = -1; 69 } else if (gaps_location >= 0) {
39 if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { 70 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) {
40 struct workspace_output *old = config->workspace_outputs->items[i]; 71 return error;
41 free(old); // workspaces can only be assigned to a single output 72 }
42 list_del(config->workspace_outputs, i); 73 char *ws_name = join_args(argv, argc - 3);
74 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
75 free(ws_name);
76 if (!wsc) {
77 return cmd_results_new(CMD_FAILURE, "workspace gaps",
78 "Unable to allocate workspace output");
79 }
80 int *prop = NULL;
81 if (strcasecmp(argv[gaps_location + 1], "inner") == 0) {
82 prop = &wsc->gaps_inner;
83 } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) {
84 prop = &wsc->gaps_outer;
85 } else {
86 return cmd_results_new(CMD_FAILURE, "workspace gaps",
87 "Expected 'workspace <ws> gaps inner|outer <px>'");
88 }
89 char *end;
90 int val = strtol(argv[gaps_location + 2], &end, 10);
91
92 if (strlen(end)) {
93 free(end);
94 return cmd_results_new(CMD_FAILURE, "workspace gaps",
95 "Expected 'workspace <ws> gaps inner|outer <px>'");
43 } 96 }
44 wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); 97 *prop = val >= 0 ? val : 0;
45 list_add(config->workspace_outputs, wso);
46 } else { 98 } else {
47 if (config->reading || !config->active) { 99 if (config->reading || !config->active) {
48 return cmd_results_new(CMD_DEFER, "workspace", NULL); 100 return cmd_results_new(CMD_DEFER, "workspace", NULL);
diff --git a/sway/config.c b/sway/config.c
index 830fb65f..1e08559d 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -95,7 +95,12 @@ void free_config(struct sway_config *config) {
95 list_free(config->bars); 95 list_free(config->bars);
96 } 96 }
97 list_free(config->cmd_queue); 97 list_free(config->cmd_queue);
98 list_free(config->workspace_outputs); 98 if (config->workspace_configs) {
99 for (int i = 0; i < config->workspace_configs->length; i++) {
100 free_workspace_config(config->workspace_configs->items[i]);
101 }
102 list_free(config->workspace_configs);
103 }
99 if (config->output_configs) { 104 if (config->output_configs) {
100 for (int i = 0; i < config->output_configs->length; i++) { 105 for (int i = 0; i < config->output_configs->length; i++) {
101 free_output_config(config->output_configs->items[i]); 106 free_output_config(config->output_configs->items[i]);
@@ -175,7 +180,7 @@ static void config_defaults(struct sway_config *config) {
175 if (!(config->symbols = create_list())) goto cleanup; 180 if (!(config->symbols = create_list())) goto cleanup;
176 if (!(config->modes = create_list())) goto cleanup; 181 if (!(config->modes = create_list())) goto cleanup;
177 if (!(config->bars = create_list())) goto cleanup; 182 if (!(config->bars = create_list())) goto cleanup;
178 if (!(config->workspace_outputs = create_list())) goto cleanup; 183 if (!(config->workspace_configs = create_list())) goto cleanup;
179 if (!(config->criteria = create_list())) goto cleanup; 184 if (!(config->criteria = create_list())) goto cleanup;
180 if (!(config->no_focus = create_list())) goto cleanup; 185 if (!(config->no_focus = create_list())) goto cleanup;
181 if (!(config->input_configs = create_list())) goto cleanup; 186 if (!(config->input_configs = create_list())) goto cleanup;
@@ -804,7 +809,7 @@ char *do_var_replacement(char *str) {
804// would compare two structs in full, while this method only compares the 809// would compare two structs in full, while this method only compares the
805// workspace. 810// workspace.
806int workspace_output_cmp_workspace(const void *a, const void *b) { 811int workspace_output_cmp_workspace(const void *a, const void *b) {
807 const struct workspace_output *wsa = a, *wsb = b; 812 const struct workspace_config *wsa = a, *wsb = b;
808 return lenient_strcmp(wsa->workspace, wsb->workspace); 813 return lenient_strcmp(wsa->workspace, wsb->workspace);
809} 814}
810 815
diff --git a/sway/config/bar.c b/sway/config/bar.c
index f83b37d1..48a632fb 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -165,7 +165,7 @@ cleanup:
165 return NULL; 165 return NULL;
166} 166}
167 167
168void invoke_swaybar(struct bar_config *bar) { 168static void invoke_swaybar(struct bar_config *bar) {
169 // Pipe to communicate errors 169 // Pipe to communicate errors
170 int filedes[2]; 170 int filedes[2];
171 if (pipe(filedes) == -1) { 171 if (pipe(filedes) == -1) {
@@ -219,27 +219,13 @@ void invoke_swaybar(struct bar_config *bar) {
219 close(filedes[1]); 219 close(filedes[1]);
220} 220}
221 221
222void load_swaybars() { 222void load_swaybars(void) {
223 for (int i = 0; i < config->bars->length; ++i) { 223 for (int i = 0; i < config->bars->length; ++i) {
224 struct bar_config *bar = config->bars->items[i]; 224 struct bar_config *bar = config->bars->items[i];
225 bool apply = false; 225 if (bar->pid != 0) {
226 if (bar->outputs) { 226 terminate_swaybar(bar->pid);
227 for (int j = 0; j < bar->outputs->length; ++j) {
228 char *o = bar->outputs->items[j];
229 if (!strcmp(o, "*") || output_by_name(o)) {
230 apply = true;
231 break;
232 }
233 }
234 } else {
235 apply = true;
236 }
237 if (apply) {
238 if (bar->pid != 0) {
239 terminate_swaybar(bar->pid);
240 }
241 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
242 invoke_swaybar(bar);
243 } 227 }
228 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
229 invoke_swaybar(bar);
244 } 230 }
245} 231}
diff --git a/sway/config/input.c b/sway/config/input.c
index ad5b96c8..794d5194 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -20,6 +20,7 @@ struct input_config *new_input_config(const char* identifier) {
20 20
21 input->tap = INT_MIN; 21 input->tap = INT_MIN;
22 input->tap_button_map = INT_MIN; 22 input->tap_button_map = INT_MIN;
23 input->drag = INT_MIN;
23 input->drag_lock = INT_MIN; 24 input->drag_lock = INT_MIN;
24 input->dwt = INT_MIN; 25 input->dwt = INT_MIN;
25 input->send_events = INT_MIN; 26 input->send_events = INT_MIN;
@@ -40,22 +41,24 @@ struct input_config *new_input_config(const char* identifier) {
40} 41}
41 42
42void merge_input_config(struct input_config *dst, struct input_config *src) { 43void merge_input_config(struct input_config *dst, struct input_config *src) {
43 if (src->identifier) {
44 free(dst->identifier);
45 dst->identifier = strdup(src->identifier);
46 }
47 if (src->accel_profile != INT_MIN) { 44 if (src->accel_profile != INT_MIN) {
48 dst->accel_profile = src->accel_profile; 45 dst->accel_profile = src->accel_profile;
49 } 46 }
50 if (src->click_method != INT_MIN) { 47 if (src->click_method != INT_MIN) {
51 dst->click_method = src->click_method; 48 dst->click_method = src->click_method;
52 } 49 }
50 if (src->drag != INT_MIN) {
51 dst->drag = src->drag;
52 }
53 if (src->drag_lock != INT_MIN) { 53 if (src->drag_lock != INT_MIN) {
54 dst->drag_lock = src->drag_lock; 54 dst->drag_lock = src->drag_lock;
55 } 55 }
56 if (src->dwt != INT_MIN) { 56 if (src->dwt != INT_MIN) {
57 dst->dwt = src->dwt; 57 dst->dwt = src->dwt;
58 } 58 }
59 if (src->left_handed != INT_MIN) {
60 dst->left_handed = src->left_handed;
61 }
59 if (src->middle_emulation != INT_MIN) { 62 if (src->middle_emulation != INT_MIN) {
60 dst->middle_emulation = src->middle_emulation; 63 dst->middle_emulation = src->middle_emulation;
61 } 64 }
@@ -125,14 +128,51 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
125 } 128 }
126} 129}
127 130
128struct input_config *copy_input_config(struct input_config *ic) { 131static void merge_wildcard_on_all(struct input_config *wildcard) {
129 struct input_config *copy = calloc(1, sizeof(struct input_config)); 132 for (int i = 0; i < config->input_configs->length; i++) {
130 if (copy == NULL) { 133 struct input_config *ic = config->input_configs->items[i];
131 wlr_log(WLR_ERROR, "could not allocate input config"); 134 if (strcmp(wildcard->identifier, ic->identifier) != 0) {
132 return NULL; 135 wlr_log(WLR_DEBUG, "Merging input * config on %s", ic->identifier);
136 merge_input_config(ic, wildcard);
137 }
133 } 138 }
134 merge_input_config(copy, ic); 139}
135 return copy; 140
141struct input_config *store_input_config(struct input_config *ic) {
142 bool wildcard = strcmp(ic->identifier, "*") == 0;
143 if (wildcard) {
144 merge_wildcard_on_all(ic);
145 }
146
147 int i = list_seq_find(config->input_configs, input_identifier_cmp,
148 ic->identifier);
149 if (i >= 0) {
150 wlr_log(WLR_DEBUG, "Merging on top of existing input config");
151 struct input_config *current = config->input_configs->items[i];
152 merge_input_config(current, ic);
153 free_input_config(ic);
154 ic = current;
155 } else if (!wildcard) {
156 wlr_log(WLR_DEBUG, "Adding non-wildcard input config");
157 i = list_seq_find(config->input_configs, input_identifier_cmp, "*");
158 if (i >= 0) {
159 wlr_log(WLR_DEBUG, "Merging on top of input * config");
160 struct input_config *current = new_input_config(ic->identifier);
161 merge_input_config(current, config->input_configs->items[i]);
162 merge_input_config(current, ic);
163 free_input_config(ic);
164 ic = current;
165 }
166 list_add(config->input_configs, ic);
167 } else {
168 // New wildcard config. Just add it
169 wlr_log(WLR_DEBUG, "Adding input * config");
170 list_add(config->input_configs, ic);
171 }
172
173 wlr_log(WLR_DEBUG, "Config stored for input %s", ic->identifier);
174
175 return ic;
136} 176}
137 177
138void free_input_config(struct input_config *ic) { 178void free_input_config(struct input_config *ic) {
diff --git a/sway/config/output.c b/sway/config/output.c
index 74d79130..6f337b66 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -237,7 +237,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
237 wlr_log(WLR_DEBUG, "Setting background for output %d to %s", 237 wlr_log(WLR_DEBUG, "Setting background for output %d to %s",
238 output_i, oc->background); 238 output_i, oc->background);
239 239
240 size_t len = snprintf(NULL, 0, "%s %d %s %s %s", 240 size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s",
241 config->swaybg_command ? config->swaybg_command : "swaybg", 241 config->swaybg_command ? config->swaybg_command : "swaybg",
242 output_i, oc->background, oc->background_option, 242 output_i, oc->background, oc->background_option,
243 oc->background_fallback ? oc->background_fallback : ""); 243 oc->background_fallback ? oc->background_fallback : "");
@@ -246,7 +246,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) {
246 wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); 246 wlr_log(WLR_DEBUG, "Unable to allocate swaybg command");
247 return; 247 return;
248 } 248 }
249 snprintf(command, len + 1, "%s %d %s %s %s", 249 snprintf(command, len + 1, "%s %d \"%s\" %s %s",
250 config->swaybg_command ? config->swaybg_command : "swaybg", 250 config->swaybg_command ? config->swaybg_command : "swaybg",
251 output_i, oc->background, oc->background_option, 251 output_i, oc->background, oc->background_option,
252 oc->background_fallback ? oc->background_fallback : ""); 252 oc->background_fallback ? oc->background_fallback : "");
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 83dac4c0..46456caf 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -30,7 +30,7 @@ struct seat_config *new_seat_config(const char* name) {
30 return seat; 30 return seat;
31} 31}
32 32
33struct seat_attachment_config *seat_attachment_config_new() { 33struct seat_attachment_config *seat_attachment_config_new(void) {
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) {
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
index 9644f4e5..16b479f9 100644
--- a/sway/debug-tree.c
+++ b/sway/debug-tree.c
@@ -120,7 +120,7 @@ static int draw_node(cairo_t *cairo, struct sway_node *node,
120 return height; 120 return height;
121} 121}
122 122
123void update_debug_tree() { 123void update_debug_tree(void) {
124 if (!debug.render_tree) { 124 if (!debug.render_tree) {
125 return; 125 return;
126 } 126 }
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index d747e279..34d99d52 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -6,6 +6,7 @@
6#include <string.h> 6#include <string.h>
7#include <time.h> 7#include <time.h>
8#include <wlr/types/wlr_buffer.h> 8#include <wlr/types/wlr_buffer.h>
9#include "sway/config.h"
9#include "sway/debug.h" 10#include "sway/debug.h"
10#include "sway/desktop.h" 11#include "sway/desktop.h"
11#include "sway/desktop/idle_inhibit_v1.h" 12#include "sway/desktop/idle_inhibit_v1.h"
@@ -30,14 +31,14 @@ struct sway_transaction_instruction {
30 struct sway_transaction *transaction; 31 struct sway_transaction *transaction;
31 struct sway_node *node; 32 struct sway_node *node;
32 union { 33 union {
33 struct sway_output_state *output_state; 34 struct sway_output_state output_state;
34 struct sway_workspace_state *workspace_state; 35 struct sway_workspace_state workspace_state;
35 struct sway_container_state *container_state; 36 struct sway_container_state container_state;
36 }; 37 };
37 uint32_t serial; 38 uint32_t serial;
38}; 39};
39 40
40static struct sway_transaction *transaction_create() { 41static struct sway_transaction *transaction_create(void) {
41 struct sway_transaction *transaction = 42 struct sway_transaction *transaction =
42 calloc(1, sizeof(struct sway_transaction)); 43 calloc(1, sizeof(struct sway_transaction));
43 if (!sway_assert(transaction, "Unable to allocate transaction")) { 44 if (!sway_assert(transaction, "Unable to allocate transaction")) {
@@ -85,14 +86,7 @@ static void transaction_destroy(struct sway_transaction *transaction) {
85 86
86static void copy_output_state(struct sway_output *output, 87static void copy_output_state(struct sway_output *output,
87 struct sway_transaction_instruction *instruction) { 88 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state = 89 struct sway_output_state *state = &instruction->output_state;
89 calloc(1, sizeof(struct sway_output_state));
90 if (!state) {
91 wlr_log(WLR_ERROR, "Could not allocate output state");
92 return;
93 }
94 instruction->output_state = state;
95
96 state->workspaces = create_list(); 90 state->workspaces = create_list();
97 list_cat(state->workspaces, output->workspaces); 91 list_cat(state->workspaces, output->workspaces);
98 92
@@ -101,13 +95,7 @@ static void copy_output_state(struct sway_output *output,
101 95
102static void copy_workspace_state(struct sway_workspace *ws, 96static void copy_workspace_state(struct sway_workspace *ws,
103 struct sway_transaction_instruction *instruction) { 97 struct sway_transaction_instruction *instruction) {
104 struct sway_workspace_state *state = 98 struct sway_workspace_state *state = &instruction->workspace_state;
105 calloc(1, sizeof(struct sway_workspace_state));
106 if (!state) {
107 wlr_log(WLR_ERROR, "Could not allocate workspace state");
108 return;
109 }
110 instruction->workspace_state = state;
111 99
112 state->fullscreen = ws->fullscreen; 100 state->fullscreen = ws->fullscreen;
113 state->x = ws->x; 101 state->x = ws->x;
@@ -137,13 +125,7 @@ static void copy_workspace_state(struct sway_workspace *ws,
137 125
138static void copy_container_state(struct sway_container *container, 126static void copy_container_state(struct sway_container *container,
139 struct sway_transaction_instruction *instruction) { 127 struct sway_transaction_instruction *instruction) {
140 struct sway_container_state *state = 128 struct sway_container_state *state = &instruction->container_state;
141 calloc(1, sizeof(struct sway_container_state));
142 if (!state) {
143 wlr_log(WLR_ERROR, "Could not allocate container state");
144 return;
145 }
146 instruction->container_state = state;
147 129
148 state->layout = container->layout; 130 state->layout = container->layout;
149 state->con_x = container->x; 131 state->con_x = container->x;
@@ -300,15 +282,15 @@ static void transaction_apply(struct sway_transaction *transaction) {
300 case N_ROOT: 282 case N_ROOT:
301 break; 283 break;
302 case N_OUTPUT: 284 case N_OUTPUT:
303 apply_output_state(node->sway_output, instruction->output_state); 285 apply_output_state(node->sway_output, &instruction->output_state);
304 break; 286 break;
305 case N_WORKSPACE: 287 case N_WORKSPACE:
306 apply_workspace_state(node->sway_workspace, 288 apply_workspace_state(node->sway_workspace,
307 instruction->workspace_state); 289 &instruction->workspace_state);
308 break; 290 break;
309 case N_CONTAINER: 291 case N_CONTAINER:
310 apply_container_state(node->sway_container, 292 apply_container_state(node->sway_container,
311 instruction->container_state); 293 &instruction->container_state);
312 break; 294 break;
313 } 295 }
314 296
@@ -334,7 +316,7 @@ static bool transaction_same_nodes(struct sway_transaction *a,
334 return true; 316 return true;
335} 317}
336 318
337static void transaction_progress_queue() { 319static void transaction_progress_queue(void) {
338 if (!server.transactions->length) { 320 if (!server.transactions->length) {
339 return; 321 return;
340 } 322 }
@@ -389,7 +371,17 @@ static bool should_configure(struct sway_node *node,
389 return false; 371 return false;
390 } 372 }
391 struct sway_container_state *cstate = &node->sway_container->current; 373 struct sway_container_state *cstate = &node->sway_container->current;
392 struct sway_container_state *istate = instruction->container_state; 374 struct sway_container_state *istate = &instruction->container_state;
375#ifdef HAVE_XWAYLAND
376 // Xwayland views are position-aware and need to be reconfigured
377 // when their position changes.
378 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
379 if (cstate->view_x != istate->view_x ||
380 cstate->view_y != istate->view_y) {
381 return true;
382 }
383 }
384#endif
393 if (cstate->view_width == istate->view_width && 385 if (cstate->view_width == istate->view_width &&
394 cstate->view_height == istate->view_height) { 386 cstate->view_height == istate->view_height) {
395 return false; 387 return false;
@@ -407,10 +399,10 @@ static void transaction_commit(struct sway_transaction *transaction) {
407 struct sway_node *node = instruction->node; 399 struct sway_node *node = instruction->node;
408 if (should_configure(node, instruction)) { 400 if (should_configure(node, instruction)) {
409 instruction->serial = view_configure(node->sway_container->view, 401 instruction->serial = view_configure(node->sway_container->view,
410 instruction->container_state->view_x, 402 instruction->container_state.view_x,
411 instruction->container_state->view_y, 403 instruction->container_state.view_y,
412 instruction->container_state->view_width, 404 instruction->container_state.view_width,
413 instruction->container_state->view_height); 405 instruction->container_state.view_height);
414 ++transaction->num_waiting; 406 ++transaction->num_waiting;
415 407
416 // From here on we are rendering a saved buffer of the view, which 408 // From here on we are rendering a saved buffer of the view, which
@@ -502,8 +494,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
502 int width, int height) { 494 int width, int height) {
503 struct sway_transaction_instruction *instruction = 495 struct sway_transaction_instruction *instruction =
504 view->container->node.instruction; 496 view->container->node.instruction;
505 if (instruction->container_state->view_width == width && 497 if (instruction->container_state.view_width == width &&
506 instruction->container_state->view_height == height) { 498 instruction->container_state.view_height == height) {
507 set_instruction_ready(instruction); 499 set_instruction_ready(instruction);
508 } 500 }
509} 501}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 00448be7..6d1ccdd7 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -401,7 +401,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
401 } else { 401 } else {
402 if (view->container->parent) { 402 if (view->container->parent) {
403 arrange_container(view->container->parent); 403 arrange_container(view->container->parent);
404 } else { 404 } else if (view->container->workspace) {
405 arrange_workspace(view->container->workspace); 405 arrange_workspace(view->container->workspace);
406 } 406 }
407 } 407 }
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index d2c9a68b..95ca396c 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -398,7 +398,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
398 } else { 398 } else {
399 if (view->container->parent) { 399 if (view->container->parent) {
400 arrange_container(view->container->parent); 400 arrange_container(view->container->parent);
401 } else { 401 } else if (view->container->workspace) {
402 arrange_workspace(view->container->workspace); 402 arrange_workspace(view->container->workspace);
403 } 403 }
404 } 404 }
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 3619f202..a12ac854 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -394,7 +394,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
394 } else { 394 } else {
395 if (view->container->parent) { 395 if (view->container->parent) {
396 arrange_container(view->container->parent); 396 arrange_container(view->container->parent);
397 } else { 397 } else if (view->container->workspace) {
398 arrange_workspace(view->container->workspace); 398 arrange_workspace(view->container->workspace);
399 } 399 }
400 } 400 }
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 85951c09..3ddc27a0 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -30,7 +30,7 @@
30// when dragging to the edge of a layout container. 30// when dragging to the edge of a layout container.
31#define DROP_LAYOUT_BORDER 30 31#define DROP_LAYOUT_BORDER 30
32 32
33static uint32_t get_current_time_msec() { 33static uint32_t get_current_time_msec(void) {
34 struct timespec now; 34 struct timespec now;
35 clock_gettime(CLOCK_MONOTONIC, &now); 35 clock_gettime(CLOCK_MONOTONIC, &now);
36 return now.tv_nsec / 1000; 36 return now.tv_nsec / 1000;
@@ -897,7 +897,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
897 897
898 // Handle moving a tiling container 898 // Handle moving a tiling container
899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED &&
900 !is_floating_or_child && !cont->is_fullscreen) { 900 !is_floating_or_child && cont && !cont->is_fullscreen) {
901 seat_pointer_notify_button(seat, time_msec, button, state); 901 seat_pointer_notify_button(seat, time_msec, button, state);
902 seat_begin_move_tiling(seat, cont, button); 902 seat_begin_move_tiling(seat, cont, button);
903 return; 903 return;
@@ -911,9 +911,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
911 return; 911 return;
912 } 912 }
913 913
914 // Handle clicking a container surface 914 // Handle clicking a container surface or decorations
915 if (cont) { 915 if (cont) {
916 seat_set_focus_container(seat, cont); 916 node = seat_get_focus_inactive(seat, &cont->node);
917 seat_set_focus(seat, node);
917 seat_pointer_notify_button(seat, time_msec, button, state); 918 seat_pointer_notify_button(seat, time_msec, button, state);
918 return; 919 return;
919 } 920 }
@@ -930,12 +931,52 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
930 transaction_commit_dirty(); 931 transaction_commit_dirty();
931} 932}
932 933
934static void dispatch_cursor_axis(struct sway_cursor *cursor,
935 struct wlr_event_pointer_axis *event) {
936 struct sway_seat *seat = cursor->seat;
937
938 // Determine what's under the cursor
939 struct wlr_surface *surface = NULL;
940 double sx, sy;
941 struct sway_node *node = node_at_coords(seat,
942 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
943 struct sway_container *cont = node && node->type == N_CONTAINER ?
944 node->sway_container : NULL;
945 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
946 bool on_border = edge != WLR_EDGE_NONE;
947 bool on_titlebar = cont && !on_border && !surface;
948
949 // Scrolling on a tabbed or stacked title bar
950 if (on_titlebar) {
951 enum sway_container_layout layout = container_parent_layout(cont);
952 if (layout == L_TABBED || layout == L_STACKED) {
953 struct sway_node *active =
954 seat_get_active_tiling_child(seat, node_get_parent(node));
955 list_t *siblings = container_get_siblings(cont);
956 int desired = list_find(siblings, active->sway_container) +
957 event->delta_discrete;
958 if (desired < 0) {
959 desired = 0;
960 } else if (desired >= siblings->length) {
961 desired = siblings->length - 1;
962 }
963 struct sway_container *new_focus = siblings->items[desired];
964 node = seat_get_focus_inactive(seat, &new_focus->node);
965 seat_set_focus(seat, node);
966 return;
967 }
968 }
969
970 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
971 event->orientation, event->delta, event->delta_discrete, event->source);
972}
973
933static void handle_cursor_axis(struct wl_listener *listener, void *data) { 974static void handle_cursor_axis(struct wl_listener *listener, void *data) {
934 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 975 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
935 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); 976 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
936 struct wlr_event_pointer_axis *event = data; 977 struct wlr_event_pointer_axis *event = data;
937 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 978 dispatch_cursor_axis(cursor, event);
938 event->orientation, event->delta, event->delta_discrete, event->source); 979 transaction_commit_dirty();
939} 980}
940 981
941static void handle_touch_down(struct wl_listener *listener, void *data) { 982static void handle_touch_down(struct wl_listener *listener, void *data) {
@@ -965,8 +1006,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
965 if (seat_is_input_allowed(seat, surface)) { 1006 if (seat_is_input_allowed(seat, surface)) {
966 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, 1007 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
967 event->touch_id, sx, sy); 1008 event->touch_id, sx, sy);
968 cursor->image_client = NULL; 1009 cursor_set_image(cursor, NULL, NULL);
969 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
970 } 1010 }
971} 1011}
972 1012
@@ -1134,11 +1174,13 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1134 1174
1135void cursor_set_image(struct sway_cursor *cursor, const char *image, 1175void cursor_set_image(struct sway_cursor *cursor, const char *image,
1136 struct wl_client *client) { 1176 struct wl_client *client) {
1137 if (!cursor->image || strcmp(cursor->image, image) != 0) { 1177 if (!image) {
1178 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1179 } else if (!cursor->image || strcmp(cursor->image, image) != 0) {
1138 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1180 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1139 cursor->cursor); 1181 cursor->cursor);
1140 cursor->image = image;
1141 } 1182 }
1183 cursor->image = image;
1142 cursor->image_client = client; 1184 cursor->image_client = client;
1143} 1185}
1144 1186
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b4352c6a..32f0355e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -120,6 +120,13 @@ static void input_manager_libinput_config_pointer(
120 libinput_device_config_click_set_method(libinput_device, 120 libinput_device_config_click_set_method(libinput_device,
121 ic->click_method); 121 ic->click_method);
122 } 122 }
123 if (ic->drag != INT_MIN) {
124 wlr_log(WLR_DEBUG,
125 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)",
126 ic->identifier, ic->click_method);
127 libinput_device_config_tap_set_drag_enabled(libinput_device,
128 ic->drag);
129 }
123 if (ic->drag_lock != INT_MIN) { 130 if (ic->drag_lock != INT_MIN) {
124 wlr_log(WLR_DEBUG, 131 wlr_log(WLR_DEBUG,
125 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 132 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
@@ -233,7 +240,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
233 wlr_log(WLR_DEBUG, "adding device: '%s'", 240 wlr_log(WLR_DEBUG, "adding device: '%s'",
234 input_device->identifier); 241 input_device->identifier);
235 242
236 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 243 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
244 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
237 input_manager_libinput_config_pointer(input_device); 245 input_manager_libinput_config_pointer(input_device);
238 } 246 }
239 247
@@ -389,9 +397,12 @@ void input_manager_set_focus(struct sway_input_manager *input,
389void input_manager_apply_input_config(struct sway_input_manager *input, 397void input_manager_apply_input_config(struct sway_input_manager *input,
390 struct input_config *input_config) { 398 struct input_config *input_config) {
391 struct sway_input_device *input_device = NULL; 399 struct sway_input_device *input_device = NULL;
400 bool wildcard = strcmp(input_config->identifier, "*") == 0;
392 wl_list_for_each(input_device, &input->devices, link) { 401 wl_list_for_each(input_device, &input->devices, link) {
393 if (strcmp(input_device->identifier, input_config->identifier) == 0) { 402 if (strcmp(input_device->identifier, input_config->identifier) == 0
394 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 403 || wildcard) {
404 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
405 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
395 input_manager_libinput_config_pointer(input_device); 406 input_manager_libinput_config_pointer(input_device);
396 } 407 }
397 408
@@ -480,13 +491,16 @@ struct sway_seat *input_manager_get_default_seat(
480} 491}
481 492
482struct input_config *input_device_get_config(struct sway_input_device *device) { 493struct input_config *input_device_get_config(struct sway_input_device *device) {
494 struct input_config *wildcard_config = NULL;
483 struct input_config *input_config = NULL; 495 struct input_config *input_config = NULL;
484 for (int i = 0; i < config->input_configs->length; ++i) { 496 for (int i = 0; i < config->input_configs->length; ++i) {
485 input_config = config->input_configs->items[i]; 497 input_config = config->input_configs->items[i];
486 if (strcmp(input_config->identifier, device->identifier) == 0) { 498 if (strcmp(input_config->identifier, device->identifier) == 0) {
487 return input_config; 499 return input_config;
500 } else if (strcmp(input_config->identifier, "*") == 0) {
501 wildcard_config = input_config;
488 } 502 }
489 } 503 }
490 504
491 return NULL; 505 return wildcard_config;
492} 506}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 49fe46ba..4817eae7 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -348,17 +348,42 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
348 seat->input = input; 348 seat->input = input;
349 wl_list_init(&seat->devices); 349 wl_list_init(&seat->devices);
350 350
351 wlr_seat_set_capabilities(seat->wlr_seat,
352 WL_SEAT_CAPABILITY_KEYBOARD |
353 WL_SEAT_CAPABILITY_POINTER |
354 WL_SEAT_CAPABILITY_TOUCH);
355
356
357 wl_list_insert(&input->seats, &seat->link); 351 wl_list_insert(&input->seats, &seat->link);
358 352
359 return seat; 353 return seat;
360} 354}
361 355
356static void seat_update_capabilities(struct sway_seat *seat) {
357 uint32_t caps = 0;
358 struct sway_seat_device *seat_device;
359 wl_list_for_each(seat_device, &seat->devices, link) {
360 switch (seat_device->input_device->wlr_device->type) {
361 case WLR_INPUT_DEVICE_KEYBOARD:
362 caps |= WL_SEAT_CAPABILITY_KEYBOARD;
363 break;
364 case WLR_INPUT_DEVICE_POINTER:
365 caps |= WL_SEAT_CAPABILITY_POINTER;
366 break;
367 case WLR_INPUT_DEVICE_TOUCH:
368 caps |= WL_SEAT_CAPABILITY_TOUCH;
369 break;
370 case WLR_INPUT_DEVICE_TABLET_TOOL:
371 caps |= WL_SEAT_CAPABILITY_POINTER;
372 break;
373 case WLR_INPUT_DEVICE_TABLET_PAD:
374 break;
375 }
376 }
377 wlr_seat_set_capabilities(seat->wlr_seat, caps);
378
379 // Hide cursor if seat doesn't have pointer capability
380 if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
381 cursor_set_image(seat->cursor, NULL, NULL);
382 } else {
383 cursor_set_image(seat->cursor, "left_ptr", NULL);
384 }
385}
386
362static void seat_apply_input_config(struct sway_seat *seat, 387static void seat_apply_input_config(struct sway_seat *seat,
363 struct sway_seat_device *sway_device) { 388 struct sway_seat_device *sway_device) {
364 const char *mapped_to_output = NULL; 389 const char *mapped_to_output = NULL;
@@ -489,6 +514,8 @@ void seat_add_device(struct sway_seat *seat,
489 wl_list_insert(&seat->devices, &seat_device->link); 514 wl_list_insert(&seat->devices, &seat_device->link);
490 515
491 seat_configure_device(seat, input_device); 516 seat_configure_device(seat, input_device);
517
518 seat_update_capabilities(seat);
492} 519}
493 520
494void seat_remove_device(struct sway_seat *seat, 521void seat_remove_device(struct sway_seat *seat,
@@ -503,6 +530,8 @@ void seat_remove_device(struct sway_seat *seat,
503 input_device->identifier, seat->wlr_seat->name); 530 input_device->identifier, seat->wlr_seat->name);
504 531
505 seat_device_destroy(seat_device); 532 seat_device_destroy(seat_device);
533
534 seat_update_capabilities(seat);
506} 535}
507 536
508void seat_configure_xcursor(struct sway_seat *seat) { 537void seat_configure_xcursor(struct sway_seat *seat) {
@@ -532,8 +561,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
532 output->name, (double)output->scale); 561 output->name, (double)output->scale);
533 } 562 }
534 563
535 wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, 564 cursor_set_image(seat->cursor, "left_ptr", NULL);
536 "left_ptr", seat->cursor->cursor);
537 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 565 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
538 seat->cursor->cursor->y); 566 seat->cursor->cursor->y);
539} 567}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index f054ac9f..45915094 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -42,7 +42,7 @@ static const char *ipc_json_orientation_description(enum sway_container_layout l
42 return "none"; 42 return "none";
43} 43}
44 44
45json_object *ipc_json_get_version() { 45json_object *ipc_json_get_version(void) {
46 int major = 0, minor = 0, patch = 0; 46 int major = 0, minor = 0, patch = 0;
47 json_object *version = json_object_new_object(); 47 json_object *version = json_object_new_object();
48 48
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 8ae265f6..2d915502 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,8 +1,5 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#ifndef __FreeBSD__ 2#define _POSIX_C_SOURCE 200112L
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700
5#endif
6#ifdef __linux__ 3#ifdef __linux__
7#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
8#elif __FreeBSD__ 5#elif __FreeBSD__
@@ -89,10 +86,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
89} 86}
90 87
91void ipc_init(struct sway_server *server) { 88void ipc_init(struct sway_server *server) {
92 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 89 ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);
93 if (ipc_socket == -1) { 90 if (ipc_socket == -1) {
94 sway_abort("Unable to create IPC socket"); 91 sway_abort("Unable to create IPC socket");
95 } 92 }
93 if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) {
94 sway_abort("Unable to set CLOEXEC on IPC socket");
95 }
96 if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) {
97 sway_abort("Unable to set NONBLOCK on IPC socket");
98 }
96 99
97 ipc_sockaddr = ipc_user_sockaddr(); 100 ipc_sockaddr = ipc_user_sockaddr();
98 101
@@ -580,7 +583,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
580 switch (client->current_command) { 583 switch (client->current_command) {
581 case IPC_COMMAND: 584 case IPC_COMMAND:
582 { 585 {
583 struct cmd_results *results = execute_command(buf, NULL); 586 struct cmd_results *results = execute_command(buf, NULL, NULL);
584 transaction_commit_dirty(); 587 transaction_commit_dirty();
585 char *json = cmd_results_to_json(results); 588 char *json = cmd_results_to_json(results);
586 int length = strlen(json); 589 int length = strlen(json);
diff --git a/sway/main.c b/sway/main.c
index fb4f0d8c..dea4a31c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -12,10 +12,6 @@
12#include <sys/wait.h> 12#include <sys/wait.h>
13#include <sys/un.h> 13#include <sys/un.h>
14#include <unistd.h> 14#include <unistd.h>
15#ifdef __linux__
16#include <sys/capability.h>
17#include <sys/prctl.h>
18#endif
19#include <wlr/util/log.h> 15#include <wlr/util/log.h>
20#include "sway/commands.h" 16#include "sway/commands.h"
21#include "sway/config.h" 17#include "sway/config.h"
@@ -45,7 +41,7 @@ void sig_handler(int signal) {
45 sway_terminate(EXIT_SUCCESS); 41 sway_terminate(EXIT_SUCCESS);
46} 42}
47 43
48void detect_raspi() { 44void detect_raspi(void) {
49 bool raspi = false; 45 bool raspi = false;
50 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); 46 FILE *f = fopen("/sys/firmware/devicetree/base/model", "r");
51 if (!f) { 47 if (!f) {
@@ -85,7 +81,7 @@ void detect_raspi() {
85 } 81 }
86} 82}
87 83
88void detect_proprietary() { 84void detect_proprietary(void) {
89 FILE *f = fopen("/proc/modules", "r"); 85 FILE *f = fopen("/proc/modules", "r");
90 if (!f) { 86 if (!f) {
91 return; 87 return;
@@ -120,7 +116,7 @@ void run_as_ipc_client(char *command, char *socket_path) {
120 close(socketfd); 116 close(socketfd);
121} 117}
122 118
123static void log_env() { 119static void log_env(void) {
124 const char *log_vars[] = { 120 const char *log_vars[] = {
125 "PATH", 121 "PATH",
126 "LD_LIBRARY_PATH", 122 "LD_LIBRARY_PATH",
@@ -135,7 +131,7 @@ static void log_env() {
135 } 131 }
136} 132}
137 133
138static void log_distro() { 134static void log_distro(void) {
139 const char *paths[] = { 135 const char *paths[] = {
140 "/etc/lsb-release", 136 "/etc/lsb-release",
141 "/etc/os-release", 137 "/etc/os-release",
@@ -162,7 +158,7 @@ static void log_distro() {
162 } 158 }
163} 159}
164 160
165static void log_kernel() { 161static void log_kernel(void) {
166 FILE *f = popen("uname -a", "r"); 162 FILE *f = popen("uname -a", "r");
167 if (!f) { 163 if (!f) {
168 wlr_log(WLR_INFO, "Unable to determine kernel version"); 164 wlr_log(WLR_INFO, "Unable to determine kernel version");
@@ -181,28 +177,8 @@ static void log_kernel() {
181 pclose(f); 177 pclose(f);
182} 178}
183 179
184static void executable_sanity_check() {
185#ifdef __linux__
186 struct stat sb;
187 char *exe = realpath("/proc/self/exe", NULL);
188 stat(exe, &sb);
189 // We assume that cap_get_file returning NULL implies ENODATA
190 if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) {
191 wlr_log(WLR_ERROR,
192 "sway executable has both the s(g)uid bit AND file caps set.");
193 wlr_log(WLR_ERROR,
194 "This is strongly discouraged (and completely broken).");
195 wlr_log(WLR_ERROR,
196 "Please clear one of them (either the suid bit, or the file caps).");
197 wlr_log(WLR_ERROR,
198 "If unsure, strip the file caps.");
199 exit(EXIT_FAILURE);
200 }
201 free(exe);
202#endif
203}
204 180
205static void drop_permissions(bool keep_caps) { 181static void drop_permissions(void) {
206 if (getuid() != geteuid() || getgid() != getegid()) { 182 if (getuid() != geteuid() || getgid() != getegid()) {
207 if (setgid(getgid()) != 0) { 183 if (setgid(getgid()) != 0) {
208 wlr_log(WLR_ERROR, "Unable to drop root"); 184 wlr_log(WLR_ERROR, "Unable to drop root");
@@ -217,20 +193,6 @@ static void drop_permissions(bool keep_caps) {
217 wlr_log(WLR_ERROR, "Root privileges can be restored."); 193 wlr_log(WLR_ERROR, "Root privileges can be restored.");
218 exit(EXIT_FAILURE); 194 exit(EXIT_FAILURE);
219 } 195 }
220#ifdef __linux__
221 if (keep_caps) {
222 // Drop every cap except CAP_SYS_PTRACE
223 cap_t caps = cap_init();
224 cap_value_t keep = CAP_SYS_PTRACE;
225 wlr_log(WLR_INFO, "Dropping extra capabilities");
226 if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) ||
227 cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) ||
228 cap_set_proc(caps)) {
229 wlr_log(WLR_ERROR, "Failed to drop extra capabilities");
230 exit(EXIT_FAILURE);
231 }
232 }
233#endif
234} 196}
235 197
236void enable_debug_flag(const char *flag) { 198void enable_debug_flag(const char *flag) {
@@ -279,14 +241,6 @@ int main(int argc, char **argv) {
279 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" 241 " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
280 "\n"; 242 "\n";
281 243
282 // Security:
283 unsetenv("LD_PRELOAD");
284#ifdef _LD_LIBRARY_PATH
285 setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1);
286#else
287 unsetenv("LD_LIBRARY_PATH");
288#endif
289
290 int c; 244 int c;
291 while (1) { 245 while (1) {
292 int option_index = 0; 246 int option_index = 0;
@@ -347,7 +301,7 @@ int main(int argc, char **argv) {
347 wlr_log(WLR_ERROR, "Don't use options with the IPC client"); 301 wlr_log(WLR_ERROR, "Don't use options with the IPC client");
348 exit(EXIT_FAILURE); 302 exit(EXIT_FAILURE);
349 } 303 }
350 drop_permissions(false); 304 drop_permissions();
351 char *socket_path = getenv("SWAYSOCK"); 305 char *socket_path = getenv("SWAYSOCK");
352 if (!socket_path) { 306 if (!socket_path) {
353 wlr_log(WLR_ERROR, "Unable to retrieve socket path"); 307 wlr_log(WLR_ERROR, "Unable to retrieve socket path");
@@ -358,34 +312,17 @@ int main(int argc, char **argv) {
358 return 0; 312 return 0;
359 } 313 }
360 314
361 executable_sanity_check();
362 bool suid = false;
363
364 if (!server_privileged_prepare(&server)) { 315 if (!server_privileged_prepare(&server)) {
365 return 1; 316 return 1;
366 } 317 }
367 318
368#if defined(__linux__) || defined(__FreeBSD__)
369 if (getuid() != geteuid() || getgid() != getegid()) {
370#ifdef __linux__
371 // Retain capabilities after setuid()
372 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
373 wlr_log(WLR_ERROR, "Cannot keep caps after setuid()");
374 exit(EXIT_FAILURE);
375 }
376#endif
377 suid = true;
378 }
379#endif
380
381 log_kernel(); 319 log_kernel();
382 log_distro(); 320 log_distro();
383 detect_proprietary(); 321 detect_proprietary();
384 detect_raspi(); 322 detect_raspi();
385 323
386#if defined(__linux__) || defined(__FreeBSD__) 324 drop_permissions();
387 drop_permissions(suid); 325
388#endif
389 // handle SIGTERM signals 326 // handle SIGTERM signals
390 signal(SIGTERM, sig_handler); 327 signal(SIGTERM, sig_handler);
391 328
@@ -424,11 +361,12 @@ int main(int argc, char **argv) {
424 } 361 }
425 362
426 config->active = true; 363 config->active = true;
364 load_swaybars();
427 // Execute commands until there are none left 365 // Execute commands until there are none left
428 wlr_log(WLR_DEBUG, "Running deferred commands"); 366 wlr_log(WLR_DEBUG, "Running deferred commands");
429 while (config->cmd_queue->length) { 367 while (config->cmd_queue->length) {
430 char *line = config->cmd_queue->items[0]; 368 char *line = config->cmd_queue->items[0];
431 struct cmd_results *res = execute_command(line, NULL); 369 struct cmd_results *res = execute_command(line, NULL, NULL);
432 if (res->status != CMD_SUCCESS) { 370 if (res->status != CMD_SUCCESS) {
433 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error); 371 wlr_log(WLR_ERROR, "Error on line '%s': %s", line, res->error);
434 } 372 }
diff --git a/sway/meson.build b/sway/meson.build
index d67a4c64..6eb9a9d7 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -119,6 +119,7 @@ sway_sources = files(
119 119
120 'commands/input/accel_profile.c', 120 'commands/input/accel_profile.c',
121 'commands/input/click_method.c', 121 'commands/input/click_method.c',
122 'commands/input/drag.c',
122 'commands/input/drag_lock.c', 123 'commands/input/drag_lock.c',
123 'commands/input/dwt.c', 124 'commands/input/dwt.c',
124 'commands/input/events.c', 125 'commands/input/events.c',
@@ -164,7 +165,6 @@ sway_deps = [
164 cairo, 165 cairo,
165 gdk_pixbuf, 166 gdk_pixbuf,
166 jsonc, 167 jsonc,
167 libcap,
168 libinput, 168 libinput,
169 math, 169 math,
170 pango, 170 pango,
@@ -187,5 +187,6 @@ executable(
187 include_directories: [sway_inc], 187 include_directories: [sway_inc],
188 dependencies: sway_deps, 188 dependencies: sway_deps,
189 link_with: [lib_sway_common], 189 link_with: [lib_sway_common],
190 install_rpath : rpathdir,
190 install: true 191 install: true
191) 192)
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index a61e2829..00b9386e 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -6,8 +6,7 @@ sway-bar - bar configuration file and commands
6 6
7# DESCRIPTION 7# DESCRIPTION
8 8
9Sway allows configuring swaybar in the sway configuration file. Swaybar 9Sway allows configuring swaybar in the sway configuration file.
10commands must be used inside a _bar { }_ block in the config file.
11 10
12# COMMANDS 11# COMMANDS
13 12
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 707c36af..14f2a007 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -7,7 +7,6 @@ sway-input - input configuration file and commands
7# DESCRIPTION 7# DESCRIPTION
8 8
9Sway allows for configuration of devices within the sway configuration file. 9Sway allows for configuration of devices within the sway configuration file.
10sway-input commands must be used inside an _input { }_ block in the config.
11To obtain a list of available device identifiers, run *swaymsg -t get\_inputs*. 10To obtain a list of available device identifiers, run *swaymsg -t get\_inputs*.
12 11
13# INPUT COMMANDS 12# INPUT COMMANDS
@@ -68,6 +67,9 @@ The following commands may only be used in the configuration file.
68*input* <identifier> click\_method none|button\_areas|clickfinger 67*input* <identifier> click\_method none|button\_areas|clickfinger
69 Changes the click method for the specified device. 68 Changes the click method for the specified device.
70 69
70*input* <identifier> drag enabled|disabled
71 Enables or disables tap-and-drag for specified input device.
72
71*input* <identifier> drag\_lock enabled|disabled 73*input* <identifier> drag\_lock enabled|disabled
72 Enables or disables drag lock for specified input device. 74 Enables or disables drag lock for specified input device.
73 75
@@ -116,8 +118,7 @@ The following commands may only be used in the configuration file.
116 118
117## SEAT CONFIGURATION 119## SEAT CONFIGURATION
118 120
119Configure options for multiseat mode. sway-seat commands must be used inside a 121Configure options for multiseat mode.
120_seat { }_ block in the config.
121 122
122A *seat* is a collection of input devices that act independently of each other. 123A *seat* is a collection of input devices that act independently of each other.
123Seats are identified by name and the default seat is _seat0_ if no seats are 124Seats are identified by name and the default seat is _seat0_ if no seats are
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 927bf55c..aa5b38ab 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -19,6 +19,24 @@ bindsym Shift+XF86AudioRaiseVolume exec \\
19 pactl set-sink-volume @DEFAULT_SINK@ -1% 19 pactl set-sink-volume @DEFAULT_SINK@ -1%
20``` 20```
21 21
22Commands can also be given as a block in the form *command { <subcommands...>
23}*. Anything before the opening *{* will be prepended to the lines inside the
24block. For example:
25
26```
27output eDP-1 {
28 background ~/wallpaper.png
29 resolution 1920x1080
30}
31```
32
33is identical to
34
35```
36output eDP-1 background ~/wallpaper.png
37output eDP-1 resolution 1920x1080
38```
39
22These commands can be executed in your config file, via *swaymsg*(1), or via 40These commands can be executed in your config file, via *swaymsg*(1), or via
23the bindsym command. 41the bindsym command.
24 42
@@ -37,10 +55,8 @@ which you may only select one. *[...]* is used for optional arguments, and
37 55
38The following commands may only be used in the configuration file. 56The following commands may only be used in the configuration file.
39 57
40*bar {* <commands...> *}* 58*bar* [<bar-id>] <bar-subcommands...>
41 _commands..._ after *{* will be interpreted as bar commands. For 59 For details on bar subcommands, see *sway-bar*(5).
42 details, see *sway-bar*(5). A newline is required between *{* and the
43 first command, and *}* must be alone on a line.
44 60
45*default\_orientation* horizontal|vertical|auto 61*default\_orientation* horizontal|vertical|auto
46 Sets the default container layout for tiled containers. 62 Sets the default container layout for tiled containers.
@@ -51,10 +67,6 @@ The following commands may only be used in the configuration file.
51 *wordexp*(3) for details). The same include file can only be included once; 67 *wordexp*(3) for details). The same include file can only be included once;
52 subsequent attempts will be ignored. 68 subsequent attempts will be ignored.
53 69
54*set* $<name> <value>
55 Sets variable $_name_ to _value_. You can use the new variable in the
56 arguments of future commands.
57
58*swaybg\_command* <command> 70*swaybg\_command* <command>
59 Executes custom background _command_. Default is _swaybg_. Refer to 71 Executes custom background _command_. Default is _swaybg_. Refer to
60 *output* below for more information. 72 *output* below for more information.
@@ -407,37 +419,30 @@ The default colors are:
407 inner gap is nonzero. When _off_, gaps will only be added between views. 419 inner gap is nonzero. When _off_, gaps will only be added between views.
408 _toggle_ cannot be used in the configuration file. 420 _toggle_ cannot be used in the configuration file.
409 421
410*gaps* <amount>
411 Sets _amount_ pixels of gap between windows and around each workspace.
412
413*gaps* inner|outer <amount> 422*gaps* inner|outer <amount>
414 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the former 423 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
415 affects spacing between views and the latter affects the space around each 424 affects spacing around each view and outer affects the spacing around each
416 workspace. 425 workspace. Outer gaps are in addition to inner gaps.
426
427 This affects new workspaces only, and is used when the workspace doesn't
428 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>).
417 429
418*gaps* inner|outer all|workspace|current set|plus|minus <amount> 430*gaps* inner|outer all|current set|plus|minus <amount>
419 Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for 431 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
420 all views or workspace, _workspace_ changes gaps for all views in current 432 _current_ workspace.
421 workspace (or current workspace), and _current_ changes gaps for the current
422 view or workspace.
423 433
424*hide\_edge\_borders* none|vertical|horizontal|both|smart 434*hide\_edge\_borders* none|vertical|horizontal|both|smart
425 Hides window borders adjacent to the screen edges. Default is _none_. 435 Hides window borders adjacent to the screen edges. Default is _none_.
426 436
427*input* <input\_device> *{* <commands...> *}* 437*input* <input\_device> <input-subcommands...>
428 _commands..._ after *{* will be interpreted as input commands applying to 438 For details on input subcommands, see *sway-input*(5).
429 the specified input device. For details, see *sway-input*(5). A newline is
430 required between *{* and the first command, and *}* must be alone on a
431 line.
432 439
433 \* may be used in lieu of a specific device name to configure all input 440 \* may be used in lieu of a specific device name to configure all input
434 devices. A list of input device names may be obtained via *swaymsg -t 441 devices. A list of input device names may be obtained via *swaymsg -t
435 get\_inputs*. 442 get\_inputs*.
436 443
437*seat* <seat> *{* <commands...> *}* 444*seat* <seat> <seat-subcommands...>
438 _commands..._ after *{* will be interpreted as seat commands applying to 445 For details on seat subcommands, see *sway-input*(5).
439 the specified seat. For details, see *sway-input*(5). A newline is required
440 between *{* and the first command, and *}* must be alone on a line.
441 446
442*seat* <seat> cursor move|set <x> <y> 447*seat* <seat> cursor move|set <x> <y>
443 Move specified seat's cursor relative to current position or wrap to 448 Move specified seat's cursor relative to current position or wrap to
@@ -465,10 +470,8 @@ The default colors are:
465*mode* <mode> 470*mode* <mode>
466 Switches to the specified mode. The default mode _default_. 471 Switches to the specified mode. The default mode _default_.
467 472
468*mode* [--pango\_markup] <mode> *{* <commands...> *}* 473*mode* [--pango\_markup] <mode> <mode-subcommands...>
469 _commands..._ after *{* will be added to the specified mode. A newline is 474 The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*.
470 required between *{* and the first command, and *}* must be alone on a
471 line. Only *bindsym* and *bindcode* commands are permitted in mode blocks.
472 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango 475 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango
473 markup. 476 markup.
474 477
@@ -533,8 +536,15 @@ You may combine output commands into one, like so:
533 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch 536 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
534 537
535You can get a list of output names with *swaymsg -t get\_outputs*. You may also 538You can get a list of output names with *swaymsg -t get\_outputs*. You may also
536match any output by using the output name "\*". Be sure to add this output 539match any output by using the output name "\*".
537config after the others, or it will be matched instead of the others. 540
541*set* $<name> <value>
542 Sets variable $_name_ to _value_. You can use the new variable in the
543 arguments of future commands. When the variable is used, it can be escaped
544 with an additional $ (ie $$_name_) to have the replacement happen at run
545 time instead of when reading the config. However, it does not always make
546 sense for the variable to be replaced at run time since some arguments do
547 need to be known at config time.
538 548
539*show\_marks* yes|no 549*show\_marks* yes|no
540 If *show\_marks* is yes, marks will be displayed in the window borders. 550 If *show\_marks* is yes, marks will be displayed in the window borders.
@@ -568,6 +578,10 @@ config after the others, or it will be matched instead of the others.
568*workspace* back_and_forth 578*workspace* back_and_forth
569 Switches to the previously focused workspace. 579 Switches to the previously focused workspace.
570 580
581*workspace* <name> gaps inner|outer <amount>
582 Specifies that workspace _name_ should have the given gaps settings when it
583 is created.
584
571*workspace* <name> output <output> 585*workspace* <name> output <output>
572 Specifies that workspace _name_ should be shown on the specified _output_. 586 Specifies that workspace _name_ should be shown on the specified _output_.
573 587
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index d50be25d..373460a2 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -186,6 +186,7 @@ void arrange_workspace(struct sway_workspace *workspace) {
186 area->width, area->height, area->x, area->y); 186 area->width, area->height, area->x, area->y);
187 workspace_remove_gaps(workspace); 187 workspace_remove_gaps(workspace);
188 188
189 bool first_arrange = workspace->width == 0 && workspace->height == 0;
189 double prev_x = workspace->x; 190 double prev_x = workspace->x;
190 double prev_y = workspace->y; 191 double prev_y = workspace->y;
191 workspace->width = area->width; 192 workspace->width = area->width;
@@ -196,7 +197,7 @@ void arrange_workspace(struct sway_workspace *workspace) {
196 // Adjust any floating containers 197 // Adjust any floating containers
197 double diff_x = workspace->x - prev_x; 198 double diff_x = workspace->x - prev_x;
198 double diff_y = workspace->y - prev_y; 199 double diff_y = workspace->y - prev_y;
199 if (diff_x != 0 || diff_y != 0) { 200 if (!first_arrange && (diff_x != 0 || diff_y != 0)) {
200 for (int i = 0; i < workspace->floating->length; ++i) { 201 for (int i = 0; i < workspace->floating->length; ++i) {
201 struct sway_container *floater = workspace->floating->items[i]; 202 struct sway_container *floater = workspace->floating->items[i];
202 container_floating_translate(floater, diff_x, diff_y); 203 container_floating_translate(floater, diff_x, diff_y);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 47687744..788300cc 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -67,12 +67,10 @@ void container_destroy(struct sway_container *con) {
67 list_free(con->outputs); 67 list_free(con->outputs);
68 68
69 if (con->view) { 69 if (con->view) {
70 struct sway_view *view = con->view; 70 if (con->view->container == con) {
71 view->container = NULL; 71 con->view->container = NULL;
72 free(view->title_format); 72 }
73 view->title_format = NULL; 73 if (con->view->destroying) {
74
75 if (view->destroying) {
76 view_destroy(con->view); 74 view_destroy(con->view);
77 } 75 }
78 } 76 }
@@ -215,8 +213,7 @@ static struct sway_container *container_at_tabbed(struct sway_node *parent,
215 child_index = children->length - 1; 213 child_index = children->length - 1;
216 } 214 }
217 struct sway_container *child = children->items[child_index]; 215 struct sway_container *child = children->items[child_index];
218 struct sway_node *node = seat_get_focus_inactive(seat, &child->node); 216 return child;
219 return node->sway_container;
220 } 217 }
221 218
222 // Surfaces 219 // Surfaces
@@ -243,8 +240,7 @@ static struct sway_container *container_at_stacked(struct sway_node *parent,
243 int child_index = (ly - box.y) / title_height; 240 int child_index = (ly - box.y) / title_height;
244 if (child_index < children->length) { 241 if (child_index < children->length) {
245 struct sway_container *child = children->items[child_index]; 242 struct sway_container *child = children->items[child_index];
246 struct sway_node *node = seat_get_focus_inactive(seat, &child->node); 243 return child;
247 return node->sway_container;
248 } 244 }
249 245
250 // Surfaces 246 // Surfaces
@@ -465,11 +461,17 @@ static void update_title_texture(struct sway_container *con,
465 cairo_surface_t *surface = cairo_image_surface_create( 461 cairo_surface_t *surface = cairo_image_surface_create(
466 CAIRO_FORMAT_ARGB32, width, height); 462 CAIRO_FORMAT_ARGB32, width, height);
467 cairo_t *cairo = cairo_create(surface); 463 cairo_t *cairo = cairo_create(surface);
464 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
465 cairo_font_options_t *fo = cairo_font_options_create();
466 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
467 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
468 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
469 cairo_set_font_options(cairo, fo);
470 cairo_font_options_destroy(fo);
468 cairo_set_source_rgba(cairo, class->background[0], class->background[1], 471 cairo_set_source_rgba(cairo, class->background[0], class->background[1],
469 class->background[2], class->background[3]); 472 class->background[2], class->background[3]);
470 cairo_paint(cairo); 473 cairo_paint(cairo);
471 PangoContext *pango = pango_cairo_create_context(cairo); 474 PangoContext *pango = pango_cairo_create_context(cairo);
472 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
473 cairo_set_source_rgba(cairo, class->text[0], class->text[1], 475 cairo_set_source_rgba(cairo, class->text[0], class->text[1],
474 class->text[2], class->text[3]); 476 class->text[2], class->text[3]);
475 cairo_move_to(cairo, 0, 0); 477 cairo_move_to(cairo, 0, 0);
@@ -591,7 +593,7 @@ void container_update_representation(struct sway_container *con) {
591 } 593 }
592} 594}
593 595
594size_t container_titlebar_height() { 596size_t container_titlebar_height(void) {
595 return config->font_height + TITLEBAR_V_PADDING * 2; 597 return config->font_height + TITLEBAR_V_PADDING * 2;
596} 598}
597 599
@@ -821,9 +823,16 @@ void container_floating_move_to_center(struct sway_container *con) {
821 return; 823 return;
822 } 824 }
823 struct sway_workspace *ws = con->workspace; 825 struct sway_workspace *ws = con->workspace;
826 bool full = con->is_fullscreen;
827 if (full) {
828 container_set_fullscreen(con, false);
829 }
824 double new_lx = ws->x + (ws->width - con->width) / 2; 830 double new_lx = ws->x + (ws->width - con->width) / 2;
825 double new_ly = ws->y + (ws->height - con->height) / 2; 831 double new_ly = ws->y + (ws->height - con->height) / 2;
826 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 832 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
833 if (full) {
834 container_set_fullscreen(con, true);
835 }
827} 836}
828 837
829static bool find_urgent_iterator(struct sway_container *con, void *data) { 838static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -981,7 +990,8 @@ void container_discover_outputs(struct sway_container *con) {
981 } 990 }
982 } 991 }
983 struct sway_output *new_output = container_get_effective_output(con); 992 struct sway_output *new_output = container_get_effective_output(con);
984 double old_scale = old_output ? old_output->wlr_output->scale : -1; 993 double old_scale = old_output && old_output->enabled ?
994 old_output->wlr_output->scale : -1;
985 double new_scale = new_output ? new_output->wlr_output->scale : -1; 995 double new_scale = new_output ? new_output->wlr_output->scale : -1;
986 if (old_scale != new_scale) { 996 if (old_scale != new_scale) {
987 container_update_title_textures(con); 997 container_update_title_textures(con);
@@ -1019,7 +1029,7 @@ void container_add_gaps(struct sway_container *c) {
1019 1029
1020 struct sway_workspace *ws = c->workspace; 1030 struct sway_workspace *ws = c->workspace;
1021 1031
1022 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1032 c->current_gaps = ws->gaps_inner;
1023 c->x += c->current_gaps; 1033 c->x += c->current_gaps;
1024 c->y += c->current_gaps; 1034 c->y += c->current_gaps;
1025 c->width -= 2 * c->current_gaps; 1035 c->width -= 2 * c->current_gaps;
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 1976ad51..c3176325 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -109,12 +109,24 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
109 109
110 wl_signal_emit(&root->events.new_node, &output->node); 110 wl_signal_emit(&root->events.new_node, &output->node);
111 111
112 load_swaybars();
113
114 arrange_layers(output); 112 arrange_layers(output);
115 arrange_root(); 113 arrange_root();
116} 114}
117 115
116static void evacuate_sticky(struct sway_workspace *old_ws,
117 struct sway_output *new_output) {
118 struct sway_workspace *new_ws = output_get_active_workspace(new_output);
119 while (old_ws->floating->length) {
120 struct sway_container *sticky = old_ws->floating->items[0];
121 container_detach(sticky);
122 workspace_add_floating(new_ws, sticky);
123 container_handle_fullscreen_reparent(sticky);
124 container_floating_move_to_center(sticky);
125 ipc_event_window(sticky, "move");
126 }
127 workspace_detect_urgent(new_ws);
128}
129
118static void output_evacuate(struct sway_output *output) { 130static void output_evacuate(struct sway_output *output) {
119 if (!output->workspaces->length) { 131 if (!output->workspaces->length) {
120 return; 132 return;
@@ -132,17 +144,21 @@ static void output_evacuate(struct sway_output *output) {
132 144
133 workspace_detach(workspace); 145 workspace_detach(workspace);
134 146
135 if (workspace_is_empty(workspace)) {
136 workspace_begin_destroy(workspace);
137 continue;
138 }
139
140 struct sway_output *new_output = 147 struct sway_output *new_output =
141 workspace_output_get_highest_available(workspace, output); 148 workspace_output_get_highest_available(workspace, output);
142 if (!new_output) { 149 if (!new_output) {
143 new_output = fallback_output; 150 new_output = fallback_output;
144 } 151 }
145 152
153 if (workspace_is_empty(workspace)) {
154 // If floating is not empty, there are sticky containers to move
155 if (workspace->floating->length) {
156 evacuate_sticky(workspace, new_output);
157 }
158 workspace_begin_destroy(workspace);
159 continue;
160 }
161
146 if (new_output) { 162 if (new_output) {
147 workspace_output_add_priority(workspace, new_output); 163 workspace_output_add_priority(workspace, new_output);
148 output_add_workspace(new_output, workspace); 164 output_add_workspace(new_output, workspace);
diff --git a/sway/tree/root.c b/sway/tree/root.c
index ecc04ddb..6748e9c9 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -265,14 +265,20 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
265 // Scratchpad 265 // Scratchpad
266 for (int i = 0; i < root->scratchpad->length; ++i) { 266 for (int i = 0; i < root->scratchpad->length; ++i) {
267 struct sway_container *container = root->scratchpad->items[i]; 267 struct sway_container *container = root->scratchpad->items[i];
268 // If the container has a parent then it's visible on a workspace 268 // If the container has a workspace then it's visible on a workspace
269 // and will have been iterated in the previous for loop. So we only 269 // and will have been iterated in the previous for loop. So we only
270 // iterate the hidden scratchpad containers here. 270 // iterate the hidden scratchpad containers here.
271 if (!container->parent) { 271 if (!container->workspace) {
272 f(container, data); 272 f(container, data);
273 container_for_each_child(container, f, data); 273 container_for_each_child(container, f, data);
274 } 274 }
275 } 275 }
276
277 // Saved workspaces
278 for (int i = 0; i < root->saved_workspaces->length; ++i) {
279 struct sway_workspace *ws = root->saved_workspaces->items[i];
280 workspace_for_each_container(ws, f, data);
281 }
276} 282}
277 283
278struct sway_output *root_find_output( 284struct sway_output *root_find_output(
@@ -311,7 +317,7 @@ struct sway_container *root_find_container(
311 // Scratchpad 317 // Scratchpad
312 for (int i = 0; i < root->scratchpad->length; ++i) { 318 for (int i = 0; i < root->scratchpad->length; ++i) {
313 struct sway_container *container = root->scratchpad->items[i]; 319 struct sway_container *container = root->scratchpad->items[i];
314 if (!container->parent) { 320 if (!container->workspace) {
315 if (test(container, data)) { 321 if (test(container, data)) {
316 return container; 322 return container;
317 } 323 }
@@ -320,6 +326,15 @@ struct sway_container *root_find_container(
320 } 326 }
321 } 327 }
322 } 328 }
329
330 // Saved workspaces
331 for (int i = 0; i < root->saved_workspaces->length; ++i) {
332 struct sway_workspace *ws = root->saved_workspaces->items[i];
333 if ((result = workspace_find_container(ws, test, data))) {
334 return result;
335 }
336 }
337
323 return NULL; 338 return NULL;
324} 339}
325 340
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 4398f518..a024f325 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -391,8 +391,6 @@ static bool view_has_executed_criteria(struct sway_view *view,
391} 391}
392 392
393void view_execute_criteria(struct sway_view *view) { 393void view_execute_criteria(struct sway_view *view) {
394 struct sway_seat *seat = input_manager_current_seat(input_manager);
395 struct sway_node *prior_focus = seat_get_focus(seat);
396 list_t *criterias = criteria_for_view(view, CT_COMMAND); 394 list_t *criterias = criteria_for_view(view, CT_COMMAND);
397 for (int i = 0; i < criterias->length; i++) { 395 for (int i = 0; i < criterias->length; i++) {
398 struct criteria *criteria = criterias->items[i]; 396 struct criteria *criteria = criterias->items[i];
@@ -403,16 +401,12 @@ void view_execute_criteria(struct sway_view *view) {
403 } 401 }
404 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 402 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
405 criteria->raw, view, criteria->cmdlist); 403 criteria->raw, view, criteria->cmdlist);
406 seat_set_focus_container(seat, view->container);
407 list_add(view->executed_criteria, criteria); 404 list_add(view->executed_criteria, criteria);
408 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 405 struct cmd_results *res = execute_command(
409 if (res->status != CMD_SUCCESS) { 406 criteria->cmdlist, NULL, view->container);
410 wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);
411 }
412 free_cmd_results(res); 407 free_cmd_results(res);
413 } 408 }
414 list_free(criterias); 409 list_free(criterias);
415 seat_set_focus(seat, prior_focus);
416} 410}
417 411
418static struct sway_workspace *select_workspace(struct sway_view *view) { 412static struct sway_workspace *select_workspace(struct sway_view *view) {
@@ -785,14 +779,9 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
785} 779}
786 780
787static char *escape_title(char *buffer) { 781static char *escape_title(char *buffer) {
788 int length = escape_markup_text(buffer, NULL, 0); 782 size_t length = escape_markup_text(buffer, NULL);
789 char *escaped_title = calloc(length + 1, sizeof(char)); 783 char *escaped_title = calloc(length + 1, sizeof(char));
790 int result = escape_markup_text(buffer, escaped_title, length); 784 escape_markup_text(buffer, escaped_title);
791 if (result != length) {
792 wlr_log(WLR_ERROR, "Could not escape title: %s", buffer);
793 free(escaped_title);
794 return buffer;
795 }
796 free(buffer); 785 free(buffer);
797 return escaped_title; 786 return escaped_title;
798} 787}
@@ -1000,12 +989,16 @@ bool view_is_visible(struct sway_view *view) {
1000 floater = floater->parent; 989 floater = floater->parent;
1001 } 990 }
1002 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 991 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
992 if (!is_sticky && !workspace_is_visible(workspace)) {
993 return false;
994 }
1003 // Check view isn't in a tabbed or stacked container on an inactive tab 995 // Check view isn't in a tabbed or stacked container on an inactive tab
1004 struct sway_seat *seat = input_manager_current_seat(input_manager); 996 struct sway_seat *seat = input_manager_current_seat(input_manager);
1005 struct sway_container *con = view->container; 997 struct sway_container *con = view->container;
1006 while (con) { 998 while (con) {
1007 enum sway_container_layout layout = container_parent_layout(con); 999 enum sway_container_layout layout = container_parent_layout(con);
1008 if (layout == L_TABBED || layout == L_STACKED) { 1000 if ((layout == L_TABBED || layout == L_STACKED)
1001 && !container_is_floating(con)) {
1009 struct sway_node *parent = con->parent ? 1002 struct sway_node *parent = con->parent ?
1010 &con->parent->node : &con->workspace->node; 1003 &con->parent->node : &con->workspace->node;
1011 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1004 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
@@ -1019,10 +1012,6 @@ bool view_is_visible(struct sway_view *view) {
1019 !container_is_fullscreen_or_child(view->container)) { 1012 !container_is_fullscreen_or_child(view->container)) {
1020 return false; 1013 return false;
1021 } 1014 }
1022 // Check the workspace is visible
1023 if (!is_sticky) {
1024 return workspace_is_visible(workspace);
1025 }
1026 return true; 1015 return true;
1027} 1016}
1028 1017
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 18746430..9dd5c815 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -20,17 +20,23 @@
20#include "log.h" 20#include "log.h"
21#include "util.h" 21#include "util.h"
22 22
23struct workspace_config *workspace_find_config(const char *ws_name) {
24 for (int i = 0; i < config->workspace_configs->length; ++i) {
25 struct workspace_config *wsc = config->workspace_configs->items[i];
26 if (strcmp(wsc->workspace, ws_name) == 0) {
27 return wsc;
28 }
29 }
30 return NULL;
31}
32
23struct sway_output *workspace_get_initial_output(const char *name) { 33struct sway_output *workspace_get_initial_output(const char *name) {
24 // Search for workspace<->output pair 34 // Check workspace configs for a workspace<->output pair
25 for (int i = 0; i < config->workspace_outputs->length; ++i) { 35 struct workspace_config *wsc = workspace_find_config(name);
26 struct workspace_output *wso = config->workspace_outputs->items[i]; 36 if (wsc && wsc->output) {
27 if (strcasecmp(wso->workspace, name) == 0) { 37 struct sway_output *output = output_by_name(wsc->output);
28 // Find output to use if it exists 38 if (output) {
29 struct sway_output *output = output_by_name(wso->output); 39 return output;
30 if (output) {
31 return output;
32 }
33 break;
34 } 40 }
35 } 41 }
36 // Otherwise put it on the focused output 42 // Otherwise put it on the focused output
@@ -54,10 +60,6 @@ struct sway_workspace *workspace_create(struct sway_output *output,
54 return NULL; 60 return NULL;
55 } 61 }
56 node_init(&ws->node, N_WORKSPACE, ws); 62 node_init(&ws->node, N_WORKSPACE, ws);
57 ws->x = output->lx;
58 ws->y = output->ly;
59 ws->width = output->width;
60 ws->height = output->height;
61 ws->name = name ? strdup(name) : NULL; 63 ws->name = name ? strdup(name) : NULL;
62 ws->prev_split_layout = L_NONE; 64 ws->prev_split_layout = L_NONE;
63 ws->layout = output_get_default_layout(output); 65 ws->layout = output_get_default_layout(output);
@@ -66,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output,
66 ws->output_priority = create_list(); 68 ws->output_priority = create_list();
67 workspace_output_add_priority(ws, output); 69 workspace_output_add_priority(ws, output);
68 70
71 ws->gaps_outer = config->gaps_outer;
72 ws->gaps_inner = config->gaps_inner;
73 if (name) {
74 struct workspace_config *wsc = workspace_find_config(name);
75 if (wsc) {
76 if (wsc->gaps_outer != -1) {
77 ws->gaps_outer = wsc->gaps_outer;
78 }
79 if (wsc->gaps_inner != -1) {
80 ws->gaps_inner = wsc->gaps_inner;
81 }
82 }
83 }
84
69 output_add_workspace(output, ws); 85 output_add_workspace(output, ws);
70 output_sort_workspaces(output); 86 output_sort_workspaces(output);
71 87
@@ -125,17 +141,8 @@ void next_name_map(struct sway_container *ws, void *data) {
125 141
126static bool workspace_valid_on_output(const char *output_name, 142static bool workspace_valid_on_output(const char *output_name,
127 const char *ws_name) { 143 const char *ws_name) {
128 int i; 144 struct workspace_config *wsc = workspace_find_config(ws_name);
129 for (i = 0; i < config->workspace_outputs->length; ++i) { 145 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0;
130 struct workspace_output *wso = config->workspace_outputs->items[i];
131 if (strcasecmp(wso->workspace, ws_name) == 0) {
132 if (strcasecmp(wso->output, output_name) != 0) {
133 return false;
134 }
135 }
136 }
137
138 return true;
139} 146}
140 147
141static void workspace_name_from_binding(const struct sway_binding * binding, 148static void workspace_name_from_binding(const struct sway_binding * binding,
@@ -235,13 +242,13 @@ char *workspace_next_name(const char *output_name) {
235 workspace_name_from_binding(mode->keycode_bindings->items[i], 242 workspace_name_from_binding(mode->keycode_bindings->items[i],
236 output_name, &order, &target); 243 output_name, &order, &target);
237 } 244 }
238 for (int i = 0; i < config->workspace_outputs->length; ++i) { 245 for (int i = 0; i < config->workspace_configs->length; ++i) {
239 // Unlike with bindings, this does not guarantee order 246 // Unlike with bindings, this does not guarantee order
240 const struct workspace_output *wso = config->workspace_outputs->items[i]; 247 const struct workspace_config *wsc = config->workspace_configs->items[i];
241 if (strcmp(wso->output, output_name) == 0 248 if (wsc->output && strcmp(wsc->output, output_name) == 0
242 && workspace_by_name(wso->workspace) == NULL) { 249 && workspace_by_name(wsc->workspace) == NULL) {
243 free(target); 250 free(target);
244 target = strdup(wso->workspace); 251 target = strdup(wsc->workspace);
245 break; 252 break;
246 } 253 }
247 } 254 }
@@ -389,13 +396,11 @@ bool workspace_switch(struct sway_workspace *workspace,
389 struct sway_output *next_output = workspace->output; 396 struct sway_output *next_output = workspace->output;
390 struct sway_workspace *next_output_prev_ws = 397 struct sway_workspace *next_output_prev_ws =
391 output_get_active_workspace(next_output); 398 output_get_active_workspace(next_output);
392 bool has_sticky = false;
393 if (workspace != next_output_prev_ws) { 399 if (workspace != next_output_prev_ws) {
394 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) { 400 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) {
395 struct sway_container *floater = 401 struct sway_container *floater =
396 next_output_prev_ws->floating->items[i]; 402 next_output_prev_ws->floating->items[i];
397 if (floater->is_sticky) { 403 if (floater->is_sticky) {
398 has_sticky = true;
399 container_detach(floater); 404 container_detach(floater);
400 workspace_add_floating(workspace, floater); 405 workspace_add_floating(workspace, floater);
401 if (&floater->node == focus) { 406 if (&floater->node == focus) {
@@ -414,14 +419,6 @@ bool workspace_switch(struct sway_workspace *workspace,
414 if (next == NULL) { 419 if (next == NULL) {
415 next = &workspace->node; 420 next = &workspace->node;
416 } 421 }
417 if (has_sticky) {
418 // If there's a sticky container, we might be setting focus to the same
419 // container that's already focused, so seat_set_focus is effectively a
420 // no op. We therefore need to send the IPC event and clean up the old
421 // workspace here.
422 ipc_event_workspace(active_ws, workspace, "focus");
423 workspace_consider_destroy(active_ws);
424 }
425 seat_set_focus(seat, next); 422 seat_set_focus(seat, next);
426 arrange_workspace(workspace); 423 arrange_workspace(workspace);
427 cursor_send_pointer_motion(seat->cursor, 0, true); 424 cursor_send_pointer_motion(seat->cursor, 0, true);
@@ -649,13 +646,13 @@ void workspace_add_gaps(struct sway_workspace *ws) {
649 return; 646 return;
650 } 647 }
651 648
652 ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; 649 ws->current_gaps = ws->gaps_outer;
653 650
654 if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 651 if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
655 // We have to add inner gaps for this, because children of tabbed and 652 // We have to add inner gaps for this, because children of tabbed and
656 // stacked containers don't apply their own gaps - they assume the 653 // stacked containers don't apply their own gaps - they assume the
657 // tabbed/stacked container is using gaps. 654 // tabbed/stacked container is using gaps.
658 ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 655 ws->current_gaps += ws->gaps_inner;
659 } 656 }
660 657
661 ws->x += ws->current_gaps; 658 ws->x += ws->current_gaps;
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 3ae730f7..388c24c4 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -16,12 +16,13 @@
16#else 16#else
17#include <linux/input-event-codes.h> 17#include <linux/input-event-codes.h>
18#endif 18#endif
19#include "swaybar/render.h" 19#include "swaybar/bar.h"
20#include "swaybar/config.h" 20#include "swaybar/config.h"
21#include "swaybar/event_loop.h" 21#include "swaybar/event_loop.h"
22#include "swaybar/status_line.h" 22#include "swaybar/i3bar.h"
23#include "swaybar/bar.h"
24#include "swaybar/ipc.h" 23#include "swaybar/ipc.h"
24#include "swaybar/status_line.h"
25#include "swaybar/render.h"
25#include "ipc-client.h" 26#include "ipc-client.h"
26#include "list.h" 27#include "list.h"
27#include "log.h" 28#include "log.h"
@@ -48,8 +49,13 @@ static void swaybar_output_free(struct swaybar_output *output) {
48 return; 49 return;
49 } 50 }
50 wlr_log(WLR_DEBUG, "Removing output %s", output->name); 51 wlr_log(WLR_DEBUG, "Removing output %s", output->name);
51 zwlr_layer_surface_v1_destroy(output->layer_surface); 52 if (output->layer_surface != NULL) {
52 wl_surface_destroy(output->surface); 53 zwlr_layer_surface_v1_destroy(output->layer_surface);
54 }
55 if (output->surface != NULL) {
56 wl_surface_destroy(output->surface);
57 }
58 zxdg_output_v1_destroy(output->xdg_output);
53 wl_output_destroy(output->output); 59 wl_output_destroy(output->output);
54 destroy_buffer(&output->buffers[0]); 60 destroy_buffer(&output->buffers[0]);
55 destroy_buffer(&output->buffers[1]); 61 destroy_buffer(&output->buffers[1]);
@@ -66,6 +72,16 @@ static void swaybar_output_free(struct swaybar_output *output) {
66 free(output); 72 free(output);
67} 73}
68 74
75static void set_output_dirty(struct swaybar_output *output) {
76 if (output->frame_scheduled) {
77 output->dirty = true;
78 return;
79 }
80 if (output->surface) {
81 render_frame(output);
82 }
83}
84
69static void layer_surface_configure(void *data, 85static void layer_surface_configure(void *data,
70 struct zwlr_layer_surface_v1 *surface, 86 struct zwlr_layer_surface_v1 *surface,
71 uint32_t serial, uint32_t width, uint32_t height) { 87 uint32_t serial, uint32_t width, uint32_t height) {
@@ -73,7 +89,7 @@ static void layer_surface_configure(void *data,
73 output->width = width; 89 output->width = width;
74 output->height = height; 90 output->height = height;
75 zwlr_layer_surface_v1_ack_configure(surface, serial); 91 zwlr_layer_surface_v1_ack_configure(surface, serial);
76 render_frame(output->bar, output); 92 set_output_dirty(output);
77} 93}
78 94
79static void layer_surface_closed(void *_output, 95static void layer_surface_closed(void *_output,
@@ -283,28 +299,58 @@ const struct wl_seat_listener seat_listener = {
283 .name = seat_handle_name, 299 .name = seat_handle_name,
284}; 300};
285 301
286static void output_geometry(void *data, struct wl_output *output, int32_t x, 302static void add_layer_surface(struct swaybar_output *output) {
303 if (output->surface != NULL) {
304 return;
305 }
306 struct swaybar *bar = output->bar;
307
308 output->surface = wl_compositor_create_surface(bar->compositor);
309 assert(output->surface);
310 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
311 bar->layer_shell, output->surface, output->output,
312 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
313 assert(output->layer_surface);
314 zwlr_layer_surface_v1_add_listener(output->layer_surface,
315 &layer_surface_listener, output);
316 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
317 bar->config->position);
318}
319
320static bool bar_uses_output(struct swaybar *bar, const char *name) {
321 if (bar->config->all_outputs) {
322 return true;
323 }
324 struct config_output *coutput;
325 wl_list_for_each(coutput, &bar->config->outputs, link) {
326 if (strcmp(coutput->name, name) == 0) {
327 return true;
328 }
329 }
330 return false;
331}
332
333static void output_geometry(void *data, struct wl_output *wl_output, int32_t x,
287 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, 334 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
288 const char *make, const char *model, int32_t transform) { 335 const char *make, const char *model, int32_t transform) {
289 // Who cares 336 struct swaybar_output *output = data;
337 output->subpixel = subpixel;
290} 338}
291 339
292static void output_mode(void *data, struct wl_output *output, uint32_t flags, 340static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
293 int32_t width, int32_t height, int32_t refresh) { 341 int32_t width, int32_t height, int32_t refresh) {
294 // Who cares 342 // Who cares
295} 343}
296 344
297static void output_done(void *data, struct wl_output *output) { 345static void output_done(void *data, struct wl_output *wl_output) {
298 // Who cares 346 struct swaybar_output *output = data;
347 set_output_dirty(output);
299} 348}
300 349
301static void output_scale(void *data, struct wl_output *wl_output, 350static void output_scale(void *data, struct wl_output *wl_output,
302 int32_t factor) { 351 int32_t factor) {
303 struct swaybar_output *output = data; 352 struct swaybar_output *output = data;
304 output->scale = factor; 353 output->scale = factor;
305 if (output->surface) {
306 render_frame(output->bar, output);
307 }
308} 354}
309 355
310struct wl_output_listener output_listener = { 356struct wl_output_listener output_listener = {
@@ -326,7 +372,22 @@ static void xdg_output_handle_logical_size(void *data,
326 372
327static void xdg_output_handle_done(void *data, 373static void xdg_output_handle_done(void *data,
328 struct zxdg_output_v1 *xdg_output) { 374 struct zxdg_output_v1 *xdg_output) {
329 // Who cares 375 struct swaybar_output *output = data;
376 struct swaybar *bar = output->bar;
377
378 assert(output->name != NULL);
379 if (!bar_uses_output(bar, output->name)) {
380 swaybar_output_free(output);
381 return;
382 }
383
384 if (wl_list_empty(&output->link)) {
385 wl_list_remove(&output->link);
386 wl_list_insert(&bar->outputs, &output->link);
387
388 add_layer_surface(output);
389 set_output_dirty(output);
390 }
330} 391}
331 392
332static void xdg_output_handle_name(void *data, 393static void xdg_output_handle_name(void *data,
@@ -349,17 +410,15 @@ struct zxdg_output_v1_listener xdg_output_listener = {
349 .description = xdg_output_handle_description, 410 .description = xdg_output_handle_description,
350}; 411};
351 412
352static bool bar_uses_output(struct swaybar *bar, const char *name) { 413static void add_xdg_output(struct swaybar_output *output) {
353 if (bar->config->all_outputs) { 414 if (output->xdg_output != NULL) {
354 return true; 415 return;
355 }
356 struct config_output *coutput;
357 wl_list_for_each(coutput, &bar->config->outputs, link) {
358 if (strcmp(coutput->name, name) == 0) {
359 return true;
360 }
361 } 416 }
362 return false; 417 assert(output->bar->xdg_output_manager != NULL);
418 output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
419 output->bar->xdg_output_manager, output->output);
420 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
421 output);
363} 422}
364 423
365static void handle_global(void *data, struct wl_registry *registry, 424static void handle_global(void *data, struct wl_registry *registry,
@@ -386,7 +445,10 @@ static void handle_global(void *data, struct wl_registry *registry,
386 output->wl_name = name; 445 output->wl_name = name;
387 wl_list_init(&output->workspaces); 446 wl_list_init(&output->workspaces);
388 wl_list_init(&output->hotspots); 447 wl_list_init(&output->hotspots);
389 wl_list_insert(&bar->outputs, &output->link); 448 wl_list_init(&output->link);
449 if (bar->xdg_output_manager != NULL) {
450 add_xdg_output(output);
451 }
390 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 452 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
391 bar->layer_shell = wl_registry_bind( 453 bar->layer_shell = wl_registry_bind(
392 registry, name, &zwlr_layer_shell_v1_interface, 1); 454 registry, name, &zwlr_layer_shell_v1_interface, 1);
@@ -413,21 +475,23 @@ static const struct wl_registry_listener registry_listener = {
413 .global_remove = handle_global_remove, 475 .global_remove = handle_global_remove,
414}; 476};
415 477
416static void render_all_frames(struct swaybar *bar) { 478static void set_bar_dirty(struct swaybar *bar) {
417 struct swaybar_output *output; 479 struct swaybar_output *output;
418 wl_list_for_each(output, &bar->outputs, link) { 480 wl_list_for_each(output, &bar->outputs, link) {
419 render_frame(bar, output); 481 set_output_dirty(output);
420 } 482 }
421} 483}
422 484
423void bar_setup(struct swaybar *bar, 485bool bar_setup(struct swaybar *bar,
424 const char *socket_path, const char *bar_id) { 486 const char *socket_path, const char *bar_id) {
425 bar_init(bar); 487 bar_init(bar);
426 init_event_loop(); 488 init_event_loop();
427 489
428 bar->ipc_socketfd = ipc_open_socket(socket_path); 490 bar->ipc_socketfd = ipc_open_socket(socket_path);
429 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 491 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
430 ipc_initialize(bar, bar_id); 492 if (!ipc_initialize(bar, bar_id)) {
493 return false;
494 }
431 if (bar->config->status_command) { 495 if (bar->config->status_command) {
432 bar->status = status_line_init(bar->config->status_command); 496 bar->status = status_line_init(bar->config->status_command);
433 } 497 }
@@ -443,23 +507,10 @@ void bar_setup(struct swaybar *bar,
443 507
444 struct swaybar_output *output; 508 struct swaybar_output *output;
445 wl_list_for_each(output, &bar->outputs, link) { 509 wl_list_for_each(output, &bar->outputs, link) {
446 output->xdg_output = zxdg_output_manager_v1_get_xdg_output( 510 add_xdg_output(output);
447 bar->xdg_output_manager, output->output);
448 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
449 output);
450 } 511 }
451 wl_display_roundtrip(bar->display); 512 wl_display_roundtrip(bar->display);
452 513
453 struct swaybar_output *output_tmp;
454 wl_list_for_each_safe(output, output_tmp, &bar->outputs, link) {
455 if (!bar_uses_output(bar, output->name)) {
456 zxdg_output_v1_destroy(output->xdg_output);
457 wl_output_destroy(output->output);
458 wl_list_remove(&output->link);
459 free(output);
460 }
461 }
462
463 struct swaybar_pointer *pointer = &bar->pointer; 514 struct swaybar_pointer *pointer = &bar->pointer;
464 515
465 int max_scale = 1; 516 int max_scale = 1;
@@ -479,20 +530,9 @@ void bar_setup(struct swaybar *bar,
479 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); 530 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
480 assert(pointer->cursor_surface); 531 assert(pointer->cursor_surface);
481 532
482 wl_list_for_each(output, &bar->outputs, link) {
483 output->surface = wl_compositor_create_surface(bar->compositor);
484 assert(output->surface);
485 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
486 bar->layer_shell, output->surface, output->output,
487 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
488 assert(output->layer_surface);
489 zwlr_layer_surface_v1_add_listener(output->layer_surface,
490 &layer_surface_listener, output);
491 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
492 bar->config->position);
493 }
494 ipc_get_workspaces(bar); 533 ipc_get_workspaces(bar);
495 render_all_frames(bar); 534 set_bar_dirty(bar);
535 return true;
496} 536}
497 537
498static void display_in(int fd, short mask, void *data) { 538static void display_in(int fd, short mask, void *data) {
@@ -506,7 +546,7 @@ static void display_in(int fd, short mask, void *data) {
506static void ipc_in(int fd, short mask, void *data) { 546static void ipc_in(int fd, short mask, void *data) {
507 struct swaybar *bar = data; 547 struct swaybar *bar = data;
508 if (handle_ipc_readable(bar)) { 548 if (handle_ipc_readable(bar)) {
509 render_all_frames(bar); 549 set_bar_dirty(bar);
510 } 550 }
511} 551}
512 552
@@ -514,10 +554,10 @@ static void status_in(int fd, short mask, void *data) {
514 struct swaybar *bar = data; 554 struct swaybar *bar = data;
515 if (mask & (POLLHUP | POLLERR)) { 555 if (mask & (POLLHUP | POLLERR)) {
516 status_error(bar->status, "[error reading from status command]"); 556 status_error(bar->status, "[error reading from status command]");
517 render_all_frames(bar); 557 set_bar_dirty(bar);
518 remove_event(fd); 558 remove_event(fd);
519 } else if (status_handle_readable(bar->status)) { 559 } else if (status_handle_readable(bar->status)) {
520 render_all_frames(bar); 560 set_bar_dirty(bar);
521 } 561 }
522} 562}
523 563
@@ -529,6 +569,7 @@ void bar_run(struct swaybar *bar) {
529 } 569 }
530 while (1) { 570 while (1) {
531 event_loop_poll(); 571 event_loop_poll();
572 wl_display_flush(bar->display);
532 } 573 }
533} 574}
534 575
diff --git a/swaybar/config.c b/swaybar/config.c
index db7b0db6..4e851cca 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -22,7 +22,7 @@ uint32_t parse_position(const char *position) {
22 } 22 }
23} 23}
24 24
25struct swaybar_config *init_config() { 25struct swaybar_config *init_config(void) {
26 struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); 26 struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));
27 config->status_command = NULL; 27 config->status_command = NULL;
28 config->pango_markup = false; 28 config->pango_markup = false;
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 88404703..8e9b038b 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -6,7 +6,9 @@
6#include <string.h> 6#include <string.h>
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/util/log.h> 8#include <wlr/util/log.h>
9#include "swaybar/bar.h"
9#include "swaybar/config.h" 10#include "swaybar/config.h"
11#include "swaybar/i3bar.h"
10#include "swaybar/status_line.h" 12#include "swaybar/status_line.h"
11 13
12void i3bar_block_unref(struct i3bar_block *block) { 14void i3bar_block_unref(struct i3bar_block *block) {
@@ -117,7 +119,9 @@ bool i3bar_handle_readable(struct status_line *status) {
117 memmove(status->buffer, &status->buffer[c], status->buffer_index); 119 memmove(status->buffer, &status->buffer[c], status->buffer_index);
118 break; 120 break;
119 } else if (!isspace(status->buffer[c])) { 121 } else if (!isspace(status->buffer[c])) {
120 status_error(status, "[invalid json]"); 122 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected '[' but encountered '%c'",
123 status->buffer[c]);
124 status_error(status, "[invalid i3bar json]");
121 return true; 125 return true;
122 } 126 }
123 } 127 }
@@ -155,6 +159,8 @@ bool i3bar_handle_readable(struct status_line *status) {
155 ++buffer_pos; 159 ++buffer_pos;
156 break; 160 break;
157 } else if (!isspace(status->buffer[buffer_pos])) { 161 } else if (!isspace(status->buffer[buffer_pos])) {
162 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected ',' but encountered '%c'",
163 status->buffer[buffer_pos]);
158 status_error(status, "[invalid i3bar json]"); 164 status_error(status, "[invalid i3bar json]");
159 return true; 165 return true;
160 } 166 }
@@ -166,7 +172,8 @@ bool i3bar_handle_readable(struct status_line *status) {
166 } else { 172 } else {
167 test_object = json_tokener_parse_ex(status->tokener, 173 test_object = json_tokener_parse_ex(status->tokener,
168 &status->buffer[buffer_pos], status->buffer_index - buffer_pos); 174 &status->buffer[buffer_pos], status->buffer_index - buffer_pos);
169 if (json_tokener_get_error(status->tokener) == json_tokener_success) { 175 enum json_tokener_error err = json_tokener_get_error(status->tokener);
176 if (err == json_tokener_success) {
170 if (json_object_get_type(test_object) == json_type_array) { 177 if (json_object_get_type(test_object) == json_type_array) {
171 if (last_object) { 178 if (last_object) {
172 json_object_put(last_object); 179 json_object_put(last_object);
@@ -198,12 +205,14 @@ bool i3bar_handle_readable(struct status_line *status) {
198 continue; // look for comma without reading more input 205 continue; // look for comma without reading more input
199 } 206 }
200 buffer_pos = status->buffer_index = 0; 207 buffer_pos = status->buffer_index = 0;
201 } else if (json_tokener_get_error(status->tokener) == json_tokener_continue) { 208 } else if (err == json_tokener_continue) {
209 json_tokener_reset(status->tokener);
202 if (status->buffer_index < status->buffer_size) { 210 if (status->buffer_index < status->buffer_size) {
203 // move the object to the start of the buffer 211 // move the object to the start of the buffer
204 status->buffer_index -= buffer_pos; 212 status->buffer_index -= buffer_pos;
205 memmove(status->buffer, &status->buffer[buffer_pos], 213 memmove(status->buffer, &status->buffer[buffer_pos],
206 status->buffer_index); 214 status->buffer_index);
215 buffer_pos = 0;
207 } else { 216 } else {
208 // expand buffer 217 // expand buffer
209 status->buffer_size *= 2; 218 status->buffer_size *= 2;
@@ -217,6 +226,10 @@ bool i3bar_handle_readable(struct status_line *status) {
217 } 226 }
218 } 227 }
219 } else { 228 } else {
229 char last_char = status->buffer[status->buffer_index - 1];
230 status->buffer[status->buffer_index - 1] = '\0';
231 wlr_log(WLR_DEBUG, "Failed to parse i3bar json - %s: '%s%c'",
232 json_tokener_error_desc(err), &status->buffer[buffer_pos], last_char);
220 status_error(status, "[failed to parse i3bar json]"); 233 status_error(status, "[failed to parse i3bar json]");
221 return true; 234 return true;
222 } 235 }
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 0e60c10c..7c53a44f 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -141,9 +141,16 @@ static void ipc_parse_colors(
141 } 141 }
142} 142}
143 143
144static void ipc_parse_config( 144static bool ipc_parse_config(
145 struct swaybar_config *config, const char *payload) { 145 struct swaybar_config *config, const char *payload) {
146 json_object *bar_config = json_tokener_parse(payload); 146 json_object *bar_config = json_tokener_parse(payload);
147 json_object *success;
148 if (json_object_object_get_ex(bar_config, "success", &success)
149 && !json_object_get_boolean(success)) {
150 wlr_log(WLR_ERROR, "No bar with that ID. Use 'swaymsg -t get_bar_config to get the available bar configs.");
151 json_object_put(bar_config);
152 return false;
153 }
147 json_object *markup, *mode, *hidden_bar, *position, *status_command; 154 json_object *markup, *mode, *hidden_bar, *position, *status_command;
148 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; 155 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
149 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; 156 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
@@ -226,10 +233,10 @@ static void ipc_parse_config(
226 } 233 }
227 234
228 json_object_put(bar_config); 235 json_object_put(bar_config);
236 return true;
229} 237}
230 238
231void ipc_get_workspaces(struct swaybar *bar) { 239void ipc_get_workspaces(struct swaybar *bar) {
232 bar->focused_output = NULL;
233 struct swaybar_output *output; 240 struct swaybar_output *output;
234 wl_list_for_each(output, &bar->outputs, link) { 241 wl_list_for_each(output, &bar->outputs, link) {
235 free_workspaces(&output->workspaces); 242 free_workspaces(&output->workspaces);
@@ -312,11 +319,14 @@ static void ipc_get_outputs(struct swaybar *bar) {
312 free(res); 319 free(res);
313} 320}
314 321
315void ipc_initialize(struct swaybar *bar, const char *bar_id) { 322bool ipc_initialize(struct swaybar *bar, const char *bar_id) {
316 uint32_t len = strlen(bar_id); 323 uint32_t len = strlen(bar_id);
317 char *res = ipc_single_command(bar->ipc_socketfd, 324 char *res = ipc_single_command(bar->ipc_socketfd,
318 IPC_GET_BAR_CONFIG, bar_id, &len); 325 IPC_GET_BAR_CONFIG, bar_id, &len);
319 ipc_parse_config(bar->config, res); 326 if (!ipc_parse_config(bar->config, res)) {
327 free(res);
328 return false;
329 }
320 free(res); 330 free(res);
321 ipc_get_outputs(bar); 331 ipc_get_outputs(bar);
322 332
@@ -324,6 +334,7 @@ void ipc_initialize(struct swaybar *bar, const char *bar_id) {
324 len = strlen(subscribe); 334 len = strlen(subscribe);
325 free(ipc_single_command(bar->ipc_event_socketfd, 335 free(ipc_single_command(bar->ipc_event_socketfd,
326 IPC_SUBSCRIBE, subscribe, &len)); 336 IPC_SUBSCRIBE, subscribe, &len));
337 return true;
327} 338}
328 339
329bool handle_ipc_readable(struct swaybar *bar) { 340bool handle_ipc_readable(struct swaybar *bar) {
diff --git a/swaybar/main.c b/swaybar/main.c
index 60e4b37c..d2c579db 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -96,7 +96,10 @@ int main(int argc, char **argv) {
96 96
97 signal(SIGTERM, sig_handler); 97 signal(SIGTERM, sig_handler);
98 98
99 bar_setup(&swaybar, socket_path, bar_id); 99 if (!bar_setup(&swaybar, socket_path, bar_id)) {
100 free(socket_path);
101 return 1;
102 }
100 103
101 free(socket_path); 104 free(socket_path);
102 free(bar_id); 105 free(bar_id);
diff --git a/swaybar/meson.build b/swaybar/meson.build
index d65edb11..7a02a33f 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -24,5 +24,6 @@ executable(
24 wlroots, 24 wlroots,
25 ], 25 ],
26 link_with: [lib_sway_common, lib_sway_client], 26 link_with: [lib_sway_common, lib_sway_client],
27 install_rpath : rpathdir,
27 install: true 28 install: true
28) 29)
diff --git a/swaybar/render.c b/swaybar/render.c
index 97690338..dc31a5ea 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
2#include <limits.h> 3#include <limits.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <stdint.h> 5#include <stdint.h>
@@ -9,6 +10,7 @@
9#include "pool-buffer.h" 10#include "pool-buffer.h"
10#include "swaybar/bar.h" 11#include "swaybar/bar.h"
11#include "swaybar/config.h" 12#include "swaybar/config.h"
13#include "swaybar/i3bar.h"
12#include "swaybar/ipc.h" 14#include "swaybar/ipc.h"
13#include "swaybar/render.h" 15#include "swaybar/render.h"
14#include "swaybar/status_line.h" 16#include "swaybar/status_line.h"
@@ -19,47 +21,47 @@ static const double WS_VERTICAL_PADDING = 1.5;
19static const double BORDER_WIDTH = 1; 21static const double BORDER_WIDTH = 1;
20 22
21static uint32_t render_status_line_error(cairo_t *cairo, 23static uint32_t render_status_line_error(cairo_t *cairo,
22 struct swaybar_output *output, struct swaybar_config *config, 24 struct swaybar_output *output, double *x) {
23 const char *error, double *x, uint32_t surface_height) { 25 const char *error = output->bar->status->text;
24 if (!error) { 26 if (!error) {
25 return 0; 27 return 0;
26 } 28 }
27 29
28 uint32_t height = surface_height * output->scale; 30 uint32_t height = output->height * output->scale;
29 31
30 cairo_set_source_u32(cairo, 0xFF0000FF); 32 cairo_set_source_u32(cairo, 0xFF0000FF);
31 33
32 int margin = 3 * output->scale; 34 int margin = 3 * output->scale;
33 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 35 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
34 36
37 char *font = output->bar->config->font;
35 int text_width, text_height; 38 int text_width, text_height;
36 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 39 get_text_size(cairo, font, &text_width, &text_height, NULL,
37 output->scale, false, "%s", error); 40 output->scale, false, "%s", error);
38 41
39 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 42 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
40 uint32_t ideal_surface_height = ideal_height / output->scale; 43 uint32_t ideal_surface_height = ideal_height / output->scale;
41 if (surface_height < ideal_surface_height) { 44 if (output->height < ideal_surface_height) {
42 return ideal_surface_height; 45 return ideal_surface_height;
43 } 46 }
44 *x -= text_width + margin; 47 *x -= text_width + margin;
45 48
46 double text_y = height / 2.0 - text_height / 2.0; 49 double text_y = height / 2.0 - text_height / 2.0;
47 cairo_move_to(cairo, *x, (int)floor(text_y)); 50 cairo_move_to(cairo, *x, (int)floor(text_y));
48 pango_printf(cairo, config->font, output->scale, false, "%s", error); 51 pango_printf(cairo, font, output->scale, false, "%s", error);
49 *x -= margin; 52 *x -= margin;
50 return surface_height; 53 return output->height;
51} 54}
52 55
53static uint32_t render_status_line_text(cairo_t *cairo, 56static uint32_t render_status_line_text(cairo_t *cairo,
54 struct swaybar_output *output, struct swaybar_config *config, 57 struct swaybar_output *output, double *x) {
55 const char *text, bool focused, double *x, uint32_t surface_height) { 58 const char *text = output->bar->status->text;
56 if (!text) { 59 if (!text) {
57 return 0; 60 return 0;
58 } 61 }
59 62
60 uint32_t height = surface_height * output->scale; 63 struct swaybar_config *config = output->bar->config;
61 64 cairo_set_source_u32(cairo, output->focused ?
62 cairo_set_source_u32(cairo, focused ?
63 config->colors.focused_statusline : config->colors.statusline); 65 config->colors.focused_statusline : config->colors.statusline);
64 66
65 int text_width, text_height; 67 int text_width, text_height;
@@ -71,17 +73,18 @@ static uint32_t render_status_line_text(cairo_t *cairo,
71 73
72 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 74 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
73 uint32_t ideal_surface_height = ideal_height / output->scale; 75 uint32_t ideal_surface_height = ideal_height / output->scale;
74 if (surface_height < ideal_surface_height) { 76 if (output->height < ideal_surface_height) {
75 return ideal_surface_height; 77 return ideal_surface_height;
76 } 78 }
77 79
78 *x -= text_width + margin; 80 *x -= text_width + margin;
81 uint32_t height = output->height * output->scale;
79 double text_y = height / 2.0 - text_height / 2.0; 82 double text_y = height / 2.0 - text_height / 2.0;
80 cairo_move_to(cairo, *x, (int)floor(text_y)); 83 cairo_move_to(cairo, *x, (int)floor(text_y));
81 pango_printf(cairo, config->font, output->scale, 84 pango_printf(cairo, config->font, output->scale,
82 config->pango_markup, "%s", text); 85 config->pango_markup, "%s", text);
83 *x -= margin; 86 *x -= margin;
84 return surface_height; 87 return output->height;
85} 88}
86 89
87static void render_sharp_line(cairo_t *cairo, uint32_t color, 90static void render_sharp_line(cairo_t *cairo, uint32_t color,
@@ -121,12 +124,11 @@ static void i3bar_block_unref_callback(void *data) {
121 124
122static uint32_t render_status_block(cairo_t *cairo, 125static uint32_t render_status_block(cairo_t *cairo,
123 struct swaybar_output *output, struct i3bar_block *block, double *x, 126 struct swaybar_output *output, struct i3bar_block *block, double *x,
124 uint32_t surface_height, bool focused, bool edge) { 127 bool edge) {
125 if (!block->full_text || !*block->full_text) { 128 if (!block->full_text || !*block->full_text) {
126 return 0; 129 return 0;
127 } 130 }
128 131
129 uint32_t height = surface_height * output->scale;
130 struct swaybar_config *config = output->bar->config; 132 struct swaybar_config *config = output->bar->config;
131 133
132 int text_width, text_height; 134 int text_width, text_height;
@@ -144,7 +146,7 @@ static uint32_t render_status_block(cairo_t *cairo,
144 double block_width = width; 146 double block_width = width;
145 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 147 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
146 uint32_t ideal_surface_height = ideal_height / output->scale; 148 uint32_t ideal_surface_height = ideal_height / output->scale;
147 if (surface_height < ideal_surface_height) { 149 if (output->height < ideal_surface_height) {
148 return ideal_surface_height; 150 return ideal_surface_height;
149 } 151 }
150 152
@@ -165,7 +167,7 @@ static uint32_t render_status_block(cairo_t *cairo,
165 output->scale, false, "%s", config->sep_symbol); 167 output->scale, false, "%s", config->sep_symbol);
166 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 168 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
167 uint32_t _ideal_surface_height = _ideal_height / output->scale; 169 uint32_t _ideal_surface_height = _ideal_height / output->scale;
168 if (surface_height < _ideal_surface_height) { 170 if (output->height < _ideal_surface_height) {
169 return _ideal_surface_height; 171 return _ideal_surface_height;
170 } 172 }
171 if (sep_width > block->separator_block_width) { 173 if (sep_width > block->separator_block_width) {
@@ -177,6 +179,7 @@ static uint32_t render_status_block(cairo_t *cairo,
177 *x -= margin; 179 *x -= margin;
178 } 180 }
179 181
182 uint32_t height = output->height * output->scale;
180 if (output->bar->status->click_events) { 183 if (output->bar->status->click_events) {
181 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 184 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
182 hotspot->x = *x; 185 hotspot->x = *x;
@@ -240,7 +243,7 @@ static uint32_t render_status_block(cairo_t *cairo,
240 } 243 }
241 244
242 if (!edge && block->separator) { 245 if (!edge && block->separator) {
243 if (focused) { 246 if (output->focused) {
244 cairo_set_source_u32(cairo, config->colors.focused_separator); 247 cairo_set_source_u32(cairo, config->colors.focused_separator);
245 } else { 248 } else {
246 cairo_set_source_u32(cairo, config->colors.separator); 249 cairo_set_source_u32(cairo, config->colors.separator);
@@ -259,19 +262,16 @@ static uint32_t render_status_block(cairo_t *cairo,
259 cairo_stroke(cairo); 262 cairo_stroke(cairo);
260 } 263 }
261 } 264 }
262 return surface_height; 265 return output->height;
263} 266}
264 267
265static uint32_t render_status_line_i3bar(cairo_t *cairo, 268static uint32_t render_status_line_i3bar(cairo_t *cairo,
266 struct swaybar_config *config, struct swaybar_output *output, 269 struct swaybar_output *output, double *x) {
267 struct status_line *status, bool focused,
268 double *x, uint32_t surface_height) {
269 uint32_t max_height = 0; 270 uint32_t max_height = 0;
270 bool edge = true; 271 bool edge = true;
271 struct i3bar_block *block; 272 struct i3bar_block *block;
272 wl_list_for_each(block, &status->blocks, link) { 273 wl_list_for_each(block, &output->bar->status->blocks, link) {
273 uint32_t h = render_status_block(cairo, output, 274 uint32_t h = render_status_block(cairo, output, block, x, edge);
274 block, x, surface_height, focused, edge);
275 max_height = h > max_height ? h : max_height; 275 max_height = h > max_height ? h : max_height;
276 edge = false; 276 edge = false;
277 } 277 }
@@ -279,19 +279,15 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
279} 279}
280 280
281static uint32_t render_status_line(cairo_t *cairo, 281static uint32_t render_status_line(cairo_t *cairo,
282 struct swaybar_config *config, struct swaybar_output *output, 282 struct swaybar_output *output, double *x) {
283 struct status_line *status, bool focused, 283 struct status_line *status = output->bar->status;
284 double *x, uint32_t surface_height) {
285 switch (status->protocol) { 284 switch (status->protocol) {
286 case PROTOCOL_ERROR: 285 case PROTOCOL_ERROR:
287 return render_status_line_error(cairo, output, config, 286 return render_status_line_error(cairo, output, x);
288 status->text, x, surface_height);
289 case PROTOCOL_TEXT: 287 case PROTOCOL_TEXT:
290 return render_status_line_text(cairo, output, config, 288 return render_status_line_text(cairo, output, x);
291 status->text, focused, x, surface_height);
292 case PROTOCOL_I3BAR: 289 case PROTOCOL_I3BAR:
293 return render_status_line_i3bar(cairo, config, output, 290 return render_status_line_i3bar(cairo, output, x);
294 status, focused, x, surface_height);
295 case PROTOCOL_UNDEF: 291 case PROTOCOL_UNDEF:
296 return 0; 292 return 0;
297 } 293 }
@@ -299,10 +295,9 @@ static uint32_t render_status_line(cairo_t *cairo,
299} 295}
300 296
301static uint32_t render_binding_mode_indicator(cairo_t *cairo, 297static uint32_t render_binding_mode_indicator(cairo_t *cairo,
302 struct swaybar_output *output, struct swaybar_config *config, 298 struct swaybar_output *output, double x) {
303 const char *mode, double x, uint32_t surface_height) { 299 struct swaybar_config *config = output->bar->config;
304 uint32_t height = surface_height * output->scale; 300 const char *mode = config->mode;
305
306 int text_width, text_height; 301 int text_width, text_height;
307 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 302 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
308 output->scale, config->mode_pango_markup, 303 output->scale, config->mode_pango_markup,
@@ -315,11 +310,12 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
315 uint32_t ideal_height = text_height + ws_vertical_padding * 2 310 uint32_t ideal_height = text_height + ws_vertical_padding * 2
316 + border_width * 2; 311 + border_width * 2;
317 uint32_t ideal_surface_height = ideal_height / output->scale; 312 uint32_t ideal_surface_height = ideal_height / output->scale;
318 if (surface_height < ideal_surface_height) { 313 if (output->height < ideal_surface_height) {
319 return ideal_surface_height; 314 return ideal_surface_height;
320 } 315 }
321 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 316 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
322 317
318 uint32_t height = output->height * output->scale;
323 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 319 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
324 cairo_rectangle(cairo, x, 0, width, height); 320 cairo_rectangle(cairo, x, 0, width, height);
325 cairo_fill(cairo); 321 cairo_fill(cairo);
@@ -339,7 +335,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
339 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 335 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
340 pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, 336 pango_printf(cairo, config->font, output->scale, config->mode_pango_markup,
341 "%s", mode); 337 "%s", mode);
342 return surface_height; 338 return output->height;
343} 339}
344 340
345static const char *strip_workspace_number(const char *ws_name) { 341static const char *strip_workspace_number(const char *ws_name) {
@@ -365,8 +361,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_out
365} 361}
366 362
367static uint32_t render_workspace_button(cairo_t *cairo, 363static uint32_t render_workspace_button(cairo_t *cairo,
368 struct swaybar_output *output, struct swaybar_config *config, 364 struct swaybar_output *output,
369 struct swaybar_workspace *ws, double *x, uint32_t surface_height) { 365 struct swaybar_workspace *ws, double *x) {
366 struct swaybar_config *config = output->bar->config;
370 const char *name = ws->name; 367 const char *name = ws->name;
371 if (config->strip_workspace_numbers) { 368 if (config->strip_workspace_numbers) {
372 name = strip_workspace_number(ws->name); 369 name = strip_workspace_number(ws->name);
@@ -383,7 +380,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
383 box_colors = config->colors.inactive_workspace; 380 box_colors = config->colors.inactive_workspace;
384 } 381 }
385 382
386 uint32_t height = surface_height * output->scale; 383 uint32_t height = output->height * output->scale;
387 384
388 int text_width, text_height; 385 int text_width, text_height;
389 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 386 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -396,7 +393,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
396 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 393 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
397 + border_width * 2; 394 + border_width * 2;
398 uint32_t ideal_surface_height = ideal_height / output->scale; 395 uint32_t ideal_surface_height = ideal_height / output->scale;
399 if (surface_height < ideal_surface_height) { 396 if (output->height < ideal_surface_height) {
400 return ideal_surface_height; 397 return ideal_surface_height;
401 } 398 }
402 399
@@ -433,11 +430,11 @@ static uint32_t render_workspace_button(cairo_t *cairo,
433 wl_list_insert(&output->hotspots, &hotspot->link); 430 wl_list_insert(&output->hotspots, &hotspot->link);
434 431
435 *x += width; 432 *x += width;
436 return surface_height; 433 return output->height;
437} 434}
438 435
439static uint32_t render_to_cairo(cairo_t *cairo, 436static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
440 struct swaybar *bar, struct swaybar_output *output) { 437 struct swaybar *bar = output->bar;
441 struct swaybar_config *config = bar->config; 438 struct swaybar_config *config = bar->config;
442 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 439 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
443 if (output->focused) { 440 if (output->focused) {
@@ -457,29 +454,43 @@ static uint32_t render_to_cairo(cairo_t *cairo,
457 */ 454 */
458 double x = output->width * output->scale; 455 double x = output->width * output->scale;
459 if (bar->status) { 456 if (bar->status) {
460 uint32_t h = render_status_line(cairo, config, output, 457 uint32_t h = render_status_line(cairo, output, &x);
461 bar->status, output->focused, &x, output->height);
462 max_height = h > max_height ? h : max_height; 458 max_height = h > max_height ? h : max_height;
463 } 459 }
464 x = 0; 460 x = 0;
465 if (config->workspace_buttons) { 461 if (config->workspace_buttons) {
466 struct swaybar_workspace *ws; 462 struct swaybar_workspace *ws;
467 wl_list_for_each_reverse(ws, &output->workspaces, link) { 463 wl_list_for_each_reverse(ws, &output->workspaces, link) {
468 uint32_t h = render_workspace_button(cairo, 464 uint32_t h = render_workspace_button(cairo, output, ws, &x);
469 output, config, ws, &x, output->height);
470 max_height = h > max_height ? h : max_height; 465 max_height = h > max_height ? h : max_height;
471 } 466 }
472 } 467 }
473 if (config->binding_mode_indicator && config->mode) { 468 if (config->binding_mode_indicator && config->mode) {
474 uint32_t h = render_binding_mode_indicator(cairo, 469 uint32_t h = render_binding_mode_indicator(cairo, output, x);
475 output, config, config->mode, x, output->height);
476 max_height = h > max_height ? h : max_height; 470 max_height = h > max_height ? h : max_height;
477 } 471 }
478 472
479 return max_height > output->height ? max_height : output->height; 473 return max_height > output->height ? max_height : output->height;
480} 474}
481 475
482void render_frame(struct swaybar *bar, struct swaybar_output *output) { 476static void output_frame_handle_done(void *data, struct wl_callback *callback,
477 uint32_t time) {
478 wl_callback_destroy(callback);
479 struct swaybar_output *output = data;
480 output->frame_scheduled = false;
481 if (output->dirty) {
482 render_frame(output);
483 output->dirty = false;
484 }
485}
486
487static const struct wl_callback_listener output_frame_listener = {
488 .done = output_frame_handle_done
489};
490
491void render_frame(struct swaybar_output *output) {
492 assert(output->surface != NULL);
493
483 struct swaybar_hotspot *hotspot, *tmp; 494 struct swaybar_hotspot *hotspot, *tmp;
484 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { 495 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
485 if (hotspot->destroy) { 496 if (hotspot->destroy) {
@@ -492,13 +503,21 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
492 cairo_surface_t *recorder = cairo_recording_surface_create( 503 cairo_surface_t *recorder = cairo_recording_surface_create(
493 CAIRO_CONTENT_COLOR_ALPHA, NULL); 504 CAIRO_CONTENT_COLOR_ALPHA, NULL);
494 cairo_t *cairo = cairo_create(recorder); 505 cairo_t *cairo = cairo_create(recorder);
506 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
507 cairo_font_options_t *fo = cairo_font_options_create();
508 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
509 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
510 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->subpixel));
511 cairo_set_font_options(cairo, fo);
512 cairo_font_options_destroy(fo);
495 cairo_save(cairo); 513 cairo_save(cairo);
496 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 514 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
497 cairo_paint(cairo); 515 cairo_paint(cairo);
498 cairo_restore(cairo); 516 cairo_restore(cairo);
499 uint32_t height = render_to_cairo(cairo, bar, output); 517 uint32_t height = render_to_cairo(cairo, output);
500 if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { 518 int config_height = output->bar->config->height;
501 height = bar->config->height; 519 if (config_height >= 0 && height < (uint32_t)config_height) {
520 height = config_height;
502 } 521 }
503 if (height != output->height) { 522 if (height != output->height) {
504 // Reconfigure surface 523 // Reconfigure surface
@@ -507,14 +526,15 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
507 // TODO: this could infinite loop if the compositor assigns us a 526 // TODO: this could infinite loop if the compositor assigns us a
508 // different height than what we asked for 527 // different height than what we asked for
509 wl_surface_commit(output->surface); 528 wl_surface_commit(output->surface);
510 wl_display_roundtrip(bar->display);
511 } else if (height > 0) { 529 } else if (height > 0) {
512 // Replay recording into shm and send it off 530 // Replay recording into shm and send it off
513 output->current_buffer = get_next_buffer(bar->shm, 531 output->current_buffer = get_next_buffer(output->bar->shm,
514 output->buffers, 532 output->buffers,
515 output->width * output->scale, 533 output->width * output->scale,
516 output->height * output->scale); 534 output->height * output->scale);
517 if (!output->current_buffer) { 535 if (!output->current_buffer) {
536 cairo_surface_destroy(recorder);
537 cairo_destroy(cairo);
518 return; 538 return;
519 } 539 }
520 cairo_t *shm = output->current_buffer->cairo; 540 cairo_t *shm = output->current_buffer->cairo;
@@ -532,8 +552,12 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
532 output->current_buffer->buffer, 0, 0); 552 output->current_buffer->buffer, 0, 0);
533 wl_surface_damage(output->surface, 0, 0, 553 wl_surface_damage(output->surface, 0, 0,
534 output->width, output->height); 554 output->width, output->height);
555
556 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
557 wl_callback_add_listener(frame_callback, &output_frame_listener, output);
558 output->frame_scheduled = true;
559
535 wl_surface_commit(output->surface); 560 wl_surface_commit(output->surface);
536 wl_display_roundtrip(bar->display);
537 } 561 }
538 cairo_surface_destroy(recorder); 562 cairo_surface_destroy(recorder);
539 cairo_destroy(cairo); 563 cairo_destroy(cairo);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 401bf6f6..ed6dc7c8 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,12 +1,15 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <fcntl.h> 2#include <fcntl.h>
3#include <sys/ioctl.h>
3#include <json-c/json.h> 4#include <json-c/json.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6#include <stdio.h> 7#include <stdio.h>
7#include <unistd.h> 8#include <unistd.h>
8#include <wlr/util/log.h> 9#include <wlr/util/log.h>
10#include "swaybar/bar.h"
9#include "swaybar/config.h" 11#include "swaybar/config.h"
12#include "swaybar/i3bar.h"
10#include "swaybar/event_loop.h" 13#include "swaybar/event_loop.h"
11#include "swaybar/status_line.h" 14#include "swaybar/status_line.h"
12#include "readline.h" 15#include "readline.h"
@@ -34,18 +37,35 @@ bool status_handle_readable(struct status_line *status) {
34 switch (status->protocol) { 37 switch (status->protocol) {
35 case PROTOCOL_UNDEF: 38 case PROTOCOL_UNDEF:
36 errno = 0; 39 errno = 0;
37 read_bytes = getline(&status->buffer, 40 int available_bytes;
38 &status->buffer_size, status->read); 41 if (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) {
39 if (errno == EAGAIN) { 42 wlr_log(WLR_ERROR, "Unable to read status command output size");
40 clearerr(status->read);
41 } else if (errno) {
42 status_error(status, "[error reading from status command]"); 43 status_error(status, "[error reading from status command]");
43 return true; 44 return true;
44 } 45 }
45 46
47 if ((size_t)available_bytes + 1 > status->buffer_size) {
48 // need room for leading '\0' too
49 status->buffer_size = available_bytes + 1;
50 status->buffer = realloc(status->buffer, status->buffer_size);
51 }
52 if (status->buffer == NULL) {
53 wlr_log_errno(WLR_ERROR, "Unable to read status line");
54 status_error(status, "[error reading from status command]");
55 return true;
56 }
57
58 read_bytes = read(status->read_fd, status->buffer, available_bytes);
59 if (read_bytes != available_bytes) {
60 status_error(status, "[error reading from status command]");
61 return true;
62 }
63 status->buffer[available_bytes] = 0;
64
46 // the header must be sent completely the first time round 65 // the header must be sent completely the first time round
66 char *newline = strchr(status->buffer, '\n');
47 json_object *header, *version; 67 json_object *header, *version;
48 if (status->buffer[read_bytes - 1] == '\n' 68 if (newline != NULL
49 && (header = json_tokener_parse(status->buffer)) 69 && (header = json_tokener_parse(status->buffer))
50 && json_object_object_get_ex(header, "version", &version) 70 && json_object_object_get_ex(header, "version", &version)
51 && json_object_get_int(version) == 1) { 71 && json_object_get_int(version) == 1) {
@@ -67,8 +87,8 @@ bool status_handle_readable(struct status_line *status) {
67 87
68 wl_list_init(&status->blocks); 88 wl_list_init(&status->blocks);
69 status->tokener = json_tokener_new(); 89 status->tokener = json_tokener_new();
70 status->buffer_index = getdelim(&status->buffer, 90 status->buffer_index = strlen(newline + 1);
71 &status->buffer_size, EOF, status->read); 91 memmove(status->buffer, newline + 1, status->buffer_index + 1);
72 return i3bar_handle_readable(status); 92 return i3bar_handle_readable(status);
73 } 93 }
74 94
diff --git a/swaybg/main.c b/swaybg/main.c
index 5b0d0458..742669ef 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -73,6 +73,10 @@ static void render_frame(struct swaybg_state *state) {
73 return; 73 return;
74 } 74 }
75 cairo_t *cairo = state->current_buffer->cairo; 75 cairo_t *cairo = state->current_buffer->cairo;
76 cairo_save(cairo);
77 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
78 cairo_paint(cairo);
79 cairo_restore(cairo);
76 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { 80 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
77 cairo_set_source_u32(cairo, state->context.color); 81 cairo_set_source_u32(cairo, state->context.color);
78 cairo_paint(cairo); 82 cairo_paint(cairo);
diff --git a/swaybg/meson.build b/swaybg/meson.build
index 8704de6d..095c5488 100644
--- a/swaybg/meson.build
+++ b/swaybg/meson.build
@@ -14,5 +14,6 @@ executable(
14 wlroots, 14 wlroots,
15 ], 15 ],
16 link_with: [lib_sway_common, lib_sway_client], 16 link_with: [lib_sway_common, lib_sway_client],
17 install_rpath : rpathdir,
17 install: true 18 install: true
18) 19)
diff --git a/swayidle/main.c b/swayidle/main.c
index 678d622f..5b6c95a7 100644
--- a/swayidle/main.c
+++ b/swayidle/main.c
@@ -92,7 +92,7 @@ static int release_lock(void *data) {
92 return 0; 92 return 0;
93} 93}
94 94
95void acquire_sleep_lock() { 95void acquire_sleep_lock(void) {
96 sd_bus_message *msg = NULL; 96 sd_bus_message *msg = NULL;
97 sd_bus_error error = SD_BUS_ERROR_NULL; 97 sd_bus_error error = SD_BUS_ERROR_NULL;
98 struct sd_bus *bus; 98 struct sd_bus *bus;
@@ -161,7 +161,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) {
161 return 1; 161 return 1;
162} 162}
163 163
164void setup_sleep_listener() { 164void setup_sleep_listener(void) {
165 struct sd_bus *bus; 165 struct sd_bus *bus;
166 166
167 int ret = sd_bus_default_system(&bus); 167 int ret = sd_bus_default_system(&bus);
diff --git a/swayidle/meson.build b/swayidle/meson.build
index f62545f8..6c3ac119 100644
--- a/swayidle/meson.build
+++ b/swayidle/meson.build
@@ -14,5 +14,6 @@ executable(
14 swayidle_deps, 14 swayidle_deps,
15 ], 15 ],
16 link_with: [lib_sway_common, lib_sway_client], 16 link_with: [lib_sway_common, lib_sway_client],
17 install_rpath : rpathdir,
17 install: true 18 install: true
18) 19)
diff --git a/swaylock/main.c b/swaylock/main.c
index 668a8742..ed8c5607 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -32,7 +32,7 @@ void sway_terminate(int exit_code) {
32 exit(exit_code); 32 exit(exit_code);
33} 33}
34 34
35static void daemonize() { 35static void daemonize(void) {
36 int fds[2]; 36 int fds[2];
37 if (pipe(fds) != 0) { 37 if (pipe(fds) != 0) {
38 wlr_log(WLR_ERROR, "Failed to pipe"); 38 wlr_log(WLR_ERROR, "Failed to pipe");
@@ -195,11 +195,15 @@ void damage_state(struct swaylock_state *state) {
195 } 195 }
196} 196}
197 197
198static void handle_wl_output_geometry(void *data, struct wl_output *output, 198static void handle_wl_output_geometry(void *data, struct wl_output *wl_output,
199 int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, 199 int32_t x, int32_t y, int32_t width_mm, int32_t height_mm,
200 int32_t subpixel, const char *make, const char *model, 200 int32_t subpixel, const char *make, const char *model,
201 int32_t transform) { 201 int32_t transform) {
202 // Who cares 202 struct swaylock_surface *surface = data;
203 surface->subpixel = subpixel;
204 if (surface->state->run_display) {
205 damage_surface(surface);
206 }
203} 207}
204 208
205static void handle_wl_output_mode(void *data, struct wl_output *output, 209static void handle_wl_output_mode(void *data, struct wl_output *output,
@@ -841,6 +845,9 @@ static int load_config(char *path, struct swaylock_state *state,
841static struct swaylock_state state; 845static struct swaylock_state state;
842 846
843int main(int argc, char **argv) { 847int main(int argc, char **argv) {
848 wlr_log_init(WLR_DEBUG, NULL);
849 initialize_pw_backend();
850
844 enum line_mode line_mode = LM_LINE; 851 enum line_mode line_mode = LM_LINE;
845 state.args = (struct swaylock_args){ 852 state.args = (struct swaylock_args){
846 .mode = BACKGROUND_MODE_SOLID_COLOR, 853 .mode = BACKGROUND_MODE_SOLID_COLOR,
@@ -853,8 +860,6 @@ int main(int argc, char **argv) {
853 wl_list_init(&state.images); 860 wl_list_init(&state.images);
854 set_default_colors(&state.args.colors); 861 set_default_colors(&state.args.colors);
855 862
856 wlr_log_init(WLR_DEBUG, NULL);
857
858 char *config_path = NULL; 863 char *config_path = NULL;
859 int result = parse_options(argc, argv, NULL, NULL, &config_path); 864 int result = parse_options(argc, argv, NULL, NULL, &config_path);
860 if (result != 0) { 865 if (result != 0) {
diff --git a/swaylock/meson.build b/swaylock/meson.build
index 675b8c69..6605340b 100644
--- a/swaylock/meson.build
+++ b/swaylock/meson.build
@@ -1,26 +1,39 @@
1sysconfdir = get_option('sysconfdir') 1sysconfdir = get_option('sysconfdir')
2 2
3executable( 3dependencies = [
4 'swaylock', [ 4 cairo,
5 'main.c', 5 client_protos,
6 'password.c', 6 gdk_pixbuf,
7 'render.c', 7 math,
8 'seat.c' 8 pango,
9 ], 9 pangocairo,
10 xkbcommon,
11 wayland_client,
12 wlroots,
13]
14
15sources = [
16 'main.c',
17 'password.c',
18 'render.c',
19 'seat.c'
20]
21
22if libpam.found()
23 sources += ['pam.c']
24 dependencies += [libpam]
25else
26 warning('The swaylock binary must be setuid when compiled without libpam')
27 warning('You must do this manually post-install: chmod a+s /path/to/swaylock')
28 sources += ['shadow.c']
29endif
30
31executable('swaylock',
32 sources,
10 include_directories: [sway_inc], 33 include_directories: [sway_inc],
11 dependencies: [ 34 dependencies: dependencies,
12 cairo,
13 client_protos,
14 gdk_pixbuf,
15 libpam,
16 math,
17 pango,
18 pangocairo,
19 xkbcommon,
20 wayland_client,
21 wlroots,
22 ],
23 link_with: [lib_sway_common, lib_sway_client], 35 link_with: [lib_sway_common, lib_sway_client],
36 install_rpath : rpathdir,
24 install: true 37 install: true
25) 38)
26 39
diff --git a/swaylock/pam.c b/swaylock/pam.c
new file mode 100644
index 00000000..cac95a85
--- /dev/null
+++ b/swaylock/pam.c
@@ -0,0 +1,62 @@
1#define _XOPEN_SOURCE 500
2#include <pwd.h>
3#include <security/pam_appl.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <wlr/util/log.h>
9#include "swaylock/swaylock.h"
10
11void initialize_pw_backend(void) {
12 // TODO: only call pam_start once. keep the same handle the whole time
13}
14
15static int function_conversation(int num_msg, const struct pam_message **msg,
16 struct pam_response **resp, void *data) {
17 struct swaylock_password *pw = data;
18 /* PAM expects an array of responses, one for each message */
19 struct pam_response *pam_reply = calloc(
20 num_msg, sizeof(struct pam_response));
21 *resp = pam_reply;
22 for (int i = 0; i < num_msg; ++i) {
23 switch (msg[i]->msg_style) {
24 case PAM_PROMPT_ECHO_OFF:
25 case PAM_PROMPT_ECHO_ON:
26 pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this
27 break;
28 case PAM_ERROR_MSG:
29 case PAM_TEXT_INFO:
30 break;
31 }
32 }
33 return PAM_SUCCESS;
34}
35
36bool attempt_password(struct swaylock_password *pw) {
37 struct passwd *passwd = getpwuid(getuid());
38 char *username = passwd->pw_name;
39 const struct pam_conv local_conversation = {
40 function_conversation, pw
41 };
42 pam_handle_t *local_auth_handle = NULL;
43 int pam_err;
44 if ((pam_err = pam_start("swaylock", username,
45 &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
46 wlr_log(WLR_ERROR, "PAM returned error %d", pam_err);
47 }
48 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
49 wlr_log(WLR_ERROR, "pam_authenticate failed");
50 goto fail;
51 }
52 // TODO: only call pam_end once we succeed at authing. refresh tokens beforehand
53 if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
54 wlr_log(WLR_ERROR, "pam_end failed");
55 goto fail;
56 }
57 clear_password_buffer(pw);
58 return true;
59fail:
60 clear_password_buffer(pw);
61 return false;
62}
diff --git a/swaylock/password.c b/swaylock/password.c
index 7c686b34..6a956bcb 100644
--- a/swaylock/password.c
+++ b/swaylock/password.c
@@ -1,7 +1,6 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <assert.h> 2#include <assert.h>
3#include <pwd.h> 3#include <pwd.h>
4#include <security/pam_appl.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <string.h> 5#include <string.h>
7#include <unistd.h> 6#include <unistd.h>
@@ -11,27 +10,6 @@
11#include "swaylock/seat.h" 10#include "swaylock/seat.h"
12#include "unicode.h" 11#include "unicode.h"
13 12
14static int function_conversation(int num_msg, const struct pam_message **msg,
15 struct pam_response **resp, void *data) {
16 struct swaylock_password *pw = data;
17 /* PAM expects an array of responses, one for each message */
18 struct pam_response *pam_reply = calloc(
19 num_msg, sizeof(struct pam_response));
20 *resp = pam_reply;
21 for (int i = 0; i < num_msg; ++i) {
22 switch (msg[i]->msg_style) {
23 case PAM_PROMPT_ECHO_OFF:
24 case PAM_PROMPT_ECHO_ON:
25 pam_reply[i].resp = strdup(pw->buffer); // PAM clears and frees this
26 break;
27 case PAM_ERROR_MSG:
28 case PAM_TEXT_INFO:
29 break;
30 }
31 }
32 return PAM_SUCCESS;
33}
34
35void clear_password_buffer(struct swaylock_password *pw) { 13void clear_password_buffer(struct swaylock_password *pw) {
36 // Use volatile keyword so so compiler can't optimize this out. 14 // Use volatile keyword so so compiler can't optimize this out.
37 volatile char *buffer = pw->buffer; 15 volatile char *buffer = pw->buffer;
@@ -42,35 +20,6 @@ void clear_password_buffer(struct swaylock_password *pw) {
42 pw->len = 0; 20 pw->len = 0;
43} 21}
44 22
45static bool attempt_password(struct swaylock_password *pw) {
46 struct passwd *passwd = getpwuid(getuid());
47 char *username = passwd->pw_name;
48 const struct pam_conv local_conversation = {
49 function_conversation, pw
50 };
51 pam_handle_t *local_auth_handle = NULL;
52 int pam_err;
53 // TODO: only call pam_start once. keep the same handle the whole time
54 if ((pam_err = pam_start("swaylock", username,
55 &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
56 wlr_log(WLR_ERROR, "PAM returned error %d", pam_err);
57 }
58 if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
59 wlr_log(WLR_ERROR, "pam_authenticate failed");
60 goto fail;
61 }
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) {
64 wlr_log(WLR_ERROR, "pam_end failed");
65 goto fail;
66 }
67 clear_password_buffer(pw);
68 return true;
69fail:
70 clear_password_buffer(pw);
71 return false;
72}
73
74static bool backspace(struct swaylock_password *pw) { 23static bool backspace(struct swaylock_password *pw) {
75 if (pw->len != 0) { 24 if (pw->len != 0) {
76 pw->buffer[--pw->len] = 0; 25 pw->buffer[--pw->len] = 0;
diff --git a/swaylock/render.c b/swaylock/render.c
index 66c55965..fa8832bd 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -39,6 +39,13 @@ void render_frame(struct swaylock_surface *surface) {
39 } 39 }
40 40
41 cairo_t *cairo = surface->current_buffer->cairo; 41 cairo_t *cairo = surface->current_buffer->cairo;
42 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
43 cairo_font_options_t *fo = cairo_font_options_create();
44 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
45 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
46 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(surface->subpixel));
47 cairo_set_font_options(cairo, fo);
48 cairo_font_options_destroy(fo);
42 cairo_identity_matrix(cairo); 49 cairo_identity_matrix(cairo);
43 50
44 cairo_save(cairo); 51 cairo_save(cairo);
diff --git a/swaylock/shadow.c b/swaylock/shadow.c
new file mode 100644
index 00000000..1f10514c
--- /dev/null
+++ b/swaylock/shadow.c
@@ -0,0 +1,128 @@
1#define _XOPEN_SOURCE
2#include <pwd.h>
3#include <shadow.h>
4#include <stdbool.h>
5#include <sys/types.h>
6#include <unistd.h>
7#include <wlr/util/log.h>
8#include "swaylock/swaylock.h"
9
10static int comm[2][2];
11
12void run_child(void) {
13 /* This code runs as root */
14 struct passwd *pwent = getpwuid(getuid());
15 if (!pwent) {
16 wlr_log_errno(WLR_ERROR, "failed to getpwuid");
17 exit(EXIT_FAILURE);
18 }
19 char *encpw = pwent->pw_passwd;
20 if (strcmp(encpw, "x") == 0) {
21 struct spwd *swent = getspnam(pwent->pw_name);
22 if (!swent) {
23 wlr_log_errno(WLR_ERROR, "failed to getspnam");
24 exit(EXIT_FAILURE);
25 }
26 encpw = swent->sp_pwdp;
27 }
28 wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name);
29
30 size_t size;
31 char *buf;
32 while (1) {
33 ssize_t amt;
34 amt = read(comm[0][0], &size, sizeof(size));
35 if (amt == 0) {
36 break;
37 } else if (amt < 0) {
38 wlr_log_errno(WLR_ERROR, "read pw request");
39 }
40 wlr_log(WLR_DEBUG, "received pw check request");
41 buf = malloc(size);
42 if (!buf) {
43 wlr_log_errno(WLR_ERROR, "failed to malloc pw buffer");
44 exit(EXIT_FAILURE);
45 }
46 size_t offs = 0;
47 do {
48 amt = read(comm[0][0], &buf[offs], size - offs);
49 if (amt <= 0) {
50 wlr_log_errno(WLR_ERROR, "failed to read pw");
51 exit(EXIT_FAILURE);
52 }
53 offs += (size_t)amt;
54 } while (offs < size);
55 bool result = false;
56 char *c = crypt(buf, encpw);
57 if (c == NULL) {
58 wlr_log_errno(WLR_ERROR, "crypt");
59 }
60 result = strcmp(c, encpw) == 0;
61 if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) {
62 wlr_log_errno(WLR_ERROR, "failed to write pw check result");
63 exit(EXIT_FAILURE);
64 }
65 free(buf);
66 }
67 exit(EXIT_SUCCESS);
68}
69
70void initialize_pw_backend(void) {
71 if (geteuid() != 0) {
72 wlr_log(WLR_ERROR, "swaylock needs to be setuid to read /etc/shadow");
73 exit(EXIT_FAILURE);
74 }
75 if (pipe(comm[0]) != 0) {
76 wlr_log_errno(WLR_ERROR, "failed to create pipe");
77 exit(EXIT_FAILURE);
78 }
79 if (pipe(comm[1]) != 0) {
80 wlr_log_errno(WLR_ERROR, "failed to create pipe");
81 exit(EXIT_FAILURE);
82 }
83 pid_t child = fork();
84 if (child == 0) {
85 close(comm[0][1]);
86 close(comm[1][0]);
87 run_child();
88 } else if (child < 0) {
89 wlr_log_errno(WLR_ERROR, "failed to fork");
90 exit(EXIT_FAILURE);
91 }
92 close(comm[0][0]);
93 close(comm[1][1]);
94 if (setgid(getgid()) != 0) {
95 wlr_log_errno(WLR_ERROR, "Unable to drop root");
96 exit(EXIT_FAILURE);
97 }
98 if (setuid(getuid()) != 0) {
99 wlr_log_errno(WLR_ERROR, "Unable to drop root");
100 exit(EXIT_FAILURE);
101 }
102}
103
104bool attempt_password(struct swaylock_password *pw) {
105 bool result = false;
106 size_t len = pw->len + 1;
107 size_t offs = 0;
108 if (write(comm[0][1], &len, sizeof(len)) < 0) {
109 wlr_log_errno(WLR_ERROR, "Failed to request pw check");
110 goto ret;
111 }
112 do {
113 ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs);
114 if (amt < 0) {
115 wlr_log_errno(WLR_ERROR, "Failed to write pw buffer");
116 goto ret;
117 }
118 offs += amt;
119 } while (offs < len);
120 if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) {
121 wlr_log_errno(WLR_ERROR, "Failed to read pw result");
122 goto ret;
123 }
124 wlr_log(WLR_DEBUG, "pw result: %d", result);
125ret:
126 clear_password_buffer(pw);
127 return result;
128}
diff --git a/swaymsg/meson.build b/swaymsg/meson.build
index 8638b838..7318349d 100644
--- a/swaymsg/meson.build
+++ b/swaymsg/meson.build
@@ -4,5 +4,6 @@ executable(
4 include_directories: [sway_inc], 4 include_directories: [sway_inc],
5 dependencies: [jsonc, wlroots], 5 dependencies: [jsonc, wlroots],
6 link_with: [lib_sway_common], 6 link_with: [lib_sway_common],
7 install_rpath : rpathdir,
7 install: true 8 install: true
8) 9)
diff --git a/swaynag/config.c b/swaynag/config.c
index 4d0824c9..cd34dcc2 100644
--- a/swaynag/config.c
+++ b/swaynag/config.c
@@ -11,7 +11,7 @@
11#include "util.h" 11#include "util.h"
12#include "wlr-layer-shell-unstable-v1-client-protocol.h" 12#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13 13
14static char *read_from_stdin() { 14static char *read_from_stdin(void) {
15 char *buffer = NULL; 15 char *buffer = NULL;
16 while (!feof(stdin)) { 16 while (!feof(stdin)) {
17 char *line = read_line(stdin); 17 char *line = read_line(stdin);
diff --git a/swaynag/meson.build b/swaynag/meson.build
index 2ba3ed95..223a0bc7 100644
--- a/swaynag/meson.build
+++ b/swaynag/meson.build
@@ -19,5 +19,6 @@ executable(
19 wlroots, 19 wlroots,
20 ], 20 ],
21 link_with: [lib_sway_common, lib_sway_client], 21 link_with: [lib_sway_common, lib_sway_client],
22 install_rpath : rpathdir,
22 install: true 23 install: true
23) 24)