aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h4
-rw-r--r--include/sway/config.h24
-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/view.h7
-rw-r--r--include/sway/tree/workspace.h9
-rw-r--r--include/swaybar/bar.h7
-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.h27
-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.h3
-rw-r--r--meson.build22
-rw-r--r--meson_options.txt3
-rw-r--r--sway/commands.c18
-rw-r--r--sway/commands/bar/binding_mode_indicator.c6
-rw-r--r--sway/commands/gaps.c306
-rw-r--r--sway/commands/hide_edge_borders.c16
-rw-r--r--sway/commands/input.c1
-rw-r--r--sway/commands/input/drag.c26
-rw-r--r--sway/commands/layout.c3
-rw-r--r--sway/commands/output/background.c18
-rw-r--r--sway/commands/resize.c30
-rw-r--r--sway/commands/smart_borders.c25
-rw-r--r--sway/commands/workspace.c74
-rw-r--r--sway/config.c12
-rw-r--r--sway/config/input.c4
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/config/seat.c2
-rw-r--r--sway/criteria.c4
-rw-r--r--sway/debug-tree.c2
-rw-r--r--sway/desktop/transaction.c55
-rw-r--r--sway/input/cursor.c11
-rw-r--r--sway/input/input-manager.c7
-rw-r--r--sway/input/seat.c63
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/main.c81
-rw-r--r--sway/meson.build4
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway-input.5.scd7
-rw-r--r--sway/sway.5.scd90
-rw-r--r--sway/tree/container.c37
-rw-r--r--sway/tree/output.c28
-rw-r--r--sway/tree/root.c15
-rw-r--r--sway/tree/view.c73
-rw-r--r--sway/tree/workspace.c79
-rw-r--r--swaybar/bar.c55
-rw-r--r--swaybar/config.c2
-rw-r--r--swaybar/i3bar.c7
-rw-r--r--swaybar/ipc.c19
-rw-r--r--swaybar/main.c5
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/render.c134
-rw-r--r--swaybar/status_line.c42
-rw-r--r--swaybg/meson.build1
-rw-r--r--swayidle/main.c4
-rw-r--r--swayidle/meson.build1
-rw-r--r--swaylock/main.c7
-rw-r--r--swaylock/meson.build51
-rw-r--r--swaylock/pam.c62
-rw-r--r--swaylock/password.c51
-rw-r--r--swaylock/shadow.c128
-rw-r--r--swaymsg/meson.build1
-rw-r--r--swaynag/config.c2
-rw-r--r--swaynag/meson.build1
70 files changed, 1131 insertions, 764 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 1654eb48..64f707f4 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -95,7 +95,7 @@ struct cmd_results *add_color(const char *name,
95/** 95/**
96 * TODO: Move this function and its dependent functions to container.c. 96 * TODO: Move this function and its dependent functions to container.c.
97 */ 97 */
98void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, 98bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge,
99 int amount); 99 int amount);
100 100
101sway_cmd cmd_assign; 101sway_cmd cmd_assign;
@@ -160,6 +160,7 @@ sway_cmd cmd_scratchpad;
160sway_cmd cmd_seamless_mouse; 160sway_cmd cmd_seamless_mouse;
161sway_cmd cmd_set; 161sway_cmd cmd_set;
162sway_cmd cmd_show_marks; 162sway_cmd cmd_show_marks;
163sway_cmd cmd_smart_borders;
163sway_cmd cmd_smart_gaps; 164sway_cmd cmd_smart_gaps;
164sway_cmd cmd_split; 165sway_cmd cmd_split;
165sway_cmd cmd_splith; 166sway_cmd cmd_splith;
@@ -217,6 +218,7 @@ sway_cmd bar_colors_cmd_urgent_workspace;
217sway_cmd input_cmd_seat; 218sway_cmd input_cmd_seat;
218sway_cmd input_cmd_accel_profile; 219sway_cmd input_cmd_accel_profile;
219sway_cmd input_cmd_click_method; 220sway_cmd input_cmd_click_method;
221sway_cmd input_cmd_drag;
220sway_cmd input_cmd_drag_lock; 222sway_cmd input_cmd_drag_lock;
221sway_cmd input_cmd_dwt; 223sway_cmd input_cmd_dwt;
222sway_cmd input_cmd_events; 224sway_cmd input_cmd_events;
diff --git a/include/sway/config.h b/include/sway/config.h
index 35f0e708..98a18b76 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 {
@@ -251,7 +253,8 @@ enum edge_border_types {
251 E_VERTICAL, /**< hide vertical edge borders */ 253 E_VERTICAL, /**< hide vertical edge borders */
252 E_HORIZONTAL, /**< hide horizontal edge borders */ 254 E_HORIZONTAL, /**< hide horizontal edge borders */
253 E_BOTH, /**< hide vertical and horizontal edge borders */ 255 E_BOTH, /**< hide vertical and horizontal edge borders */
254 E_SMART /**< hide both if precisely one window is present in workspace */ 256 E_SMART, /**< hide both if precisely one window is present in workspace */
257 E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */
255}; 258};
256 259
257enum command_context { 260enum command_context {
@@ -327,7 +330,7 @@ struct sway_config {
327 list_t *modes; 330 list_t *modes;
328 list_t *bars; 331 list_t *bars;
329 list_t *cmd_queue; 332 list_t *cmd_queue;
330 list_t *workspace_outputs; 333 list_t *workspace_configs;
331 list_t *output_configs; 334 list_t *output_configs;
332 list_t *input_configs; 335 list_t *input_configs;
333 list_t *seat_configs; 336 list_t *seat_configs;
@@ -381,6 +384,7 @@ struct sway_config {
381 int border_thickness; 384 int border_thickness;
382 int floating_border_thickness; 385 int floating_border_thickness;
383 enum edge_border_types hide_edge_borders; 386 enum edge_border_types hide_edge_borders;
387 enum edge_border_types saved_edge_borders;
384 388
385 // border colors 389 // border colors
386 struct { 390 struct {
@@ -450,8 +454,6 @@ void free_sway_variable(struct sway_variable *var);
450 */ 454 */
451char *do_var_replacement(char *str); 455char *do_var_replacement(char *str);
452 456
453struct cmd_results *check_security_config();
454
455int input_identifier_cmp(const void *item, const void *data); 457int input_identifier_cmp(const void *item, const void *data);
456 458
457struct input_config *new_input_config(const char* identifier); 459struct input_config *new_input_config(const char* identifier);
@@ -472,7 +474,7 @@ struct seat_config *copy_seat_config(struct seat_config *seat);
472 474
473void free_seat_config(struct seat_config *ic); 475void free_seat_config(struct seat_config *ic);
474 476
475struct seat_attachment_config *seat_attachment_config_new(); 477struct seat_attachment_config *seat_attachment_config_new(void);
476 478
477struct seat_attachment_config *seat_config_get_attachment( 479struct seat_attachment_config *seat_config_get_attachment(
478 struct seat_config *seat_config, char *identifier); 480 struct seat_config *seat_config, char *identifier);
@@ -518,6 +520,8 @@ struct bar_config *default_bar_config(void);
518 520
519void free_bar_config(struct bar_config *bar); 521void free_bar_config(struct bar_config *bar);
520 522
523void free_workspace_config(struct workspace_config *wsc);
524
521/** 525/**
522 * Updates the value of config->font_height based on the max title height 526 * Updates the value of config->font_height based on the max title height
523 * reported by each container. If recalculate is true, the containers will 527 * 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 6019602c..da6592b4 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/view.h b/include/sway/tree/view.h
index 2c7b4c2b..028be536 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -265,6 +265,13 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
265 int height); 265 int height);
266 266
267/** 267/**
268 * Whether or not the view is the only visible view in its tree. If the view
269 * is tiling, there may be floating views. If the view is floating, there may
270 * be tiling views or views in a different floating container.
271 */
272bool view_is_only_visible(struct sway_view *view);
273
274/**
268 * Configure the view's position and size based on the container's position and 275 * Configure the view's position and size based on the container's position and
269 * size, taking borders into consideration. 276 * size, taking borders into consideration.
270 */ 277 */
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 29e96159..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
@@ -84,6 +83,8 @@ struct swaybar_output {
84 enum wl_output_subpixel subpixel; 83 enum wl_output_subpixel subpixel;
85 struct pool_buffer buffers[2]; 84 struct pool_buffer buffers[2];
86 struct pool_buffer *current_buffer; 85 struct pool_buffer *current_buffer;
86 bool dirty;
87 bool frame_scheduled;
87}; 88};
88 89
89struct swaybar_workspace { 90struct swaybar_workspace {
@@ -95,9 +96,7 @@ struct swaybar_workspace {
95 bool urgent; 96 bool urgent;
96}; 97};
97 98
98void bar_setup(struct swaybar *bar, 99bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id);
99 const char *socket_path,
100 const char *bar_id);
101void bar_run(struct swaybar *bar); 100void bar_run(struct swaybar *bar);
102void bar_teardown(struct swaybar *bar); 101void bar_teardown(struct swaybar *bar);
103 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..ca88b0c5 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;
@@ -43,6 +23,7 @@ struct status_line {
43 struct wl_list blocks; // i3bar_block::link 23 struct wl_list blocks; // i3bar_block::link
44 24
45 bool click_events; 25 bool click_events;
26 bool clicked;
46 char *buffer; 27 char *buffer;
47 size_t buffer_size; 28 size_t buffer_size;
48 size_t buffer_index; 29 size_t buffer_index;
@@ -55,11 +36,5 @@ struct status_line *status_line_init(char *cmd);
55void status_error(struct status_line *status, const char *text); 36void status_error(struct status_line *status, const char *text);
56bool status_handle_readable(struct status_line *status); 37bool status_handle_readable(struct status_line *status);
57void status_line_free(struct status_line *status); 38void 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 39
65#endif 40#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 2f0cd34d..970e3cc9 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -101,5 +101,8 @@ void render_frame(struct swaylock_surface *surface);
101void render_frames(struct swaylock_state *state); 101void render_frames(struct swaylock_state *state);
102void damage_surface(struct swaylock_surface *surface); 102void damage_surface(struct swaylock_surface *surface);
103void 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);
104 107
105#endif 108#endif
diff --git a/meson.build b/meson.build
index 76eaff20..1e7ce281 100644
--- a/meson.build
+++ b/meson.build
@@ -42,7 +42,6 @@ pango = dependency('pango')
42pangocairo = dependency('pangocairo') 42pangocairo = dependency('pangocairo')
43gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) 43gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
44pixman = dependency('pixman-1') 44pixman = dependency('pixman-1')
45libcap = dependency('libcap', required: false)
46libinput = dependency('libinput', version: '>=1.6.0') 45libinput = dependency('libinput', version: '>=1.6.0')
47libpam = cc.find_library('pam', required: false) 46libpam = cc.find_library('pam', required: false)
48systemd = dependency('libsystemd', required: false) 47systemd = dependency('libsystemd', required: false)
@@ -74,6 +73,11 @@ if elogind.found()
74 swayidle_deps += elogind 73 swayidle_deps += elogind
75endif 74endif
76 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
77scdoc = find_program('scdoc', required: false) 81scdoc = find_program('scdoc', required: false)
78 82
79if scdoc.found() 83if scdoc.found()
@@ -124,7 +128,16 @@ else
124endif 128endif
125add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') 129add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
126 130
127add_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
128 141
129sway_inc = include_directories('include') 142sway_inc = include_directories('include')
130 143
@@ -139,10 +152,7 @@ subdir('swaybg')
139subdir('swaybar') 152subdir('swaybar')
140subdir('swayidle') 153subdir('swayidle')
141subdir('swaynag') 154subdir('swaynag')
142 155subdir('swaylock')
143if libpam.found()
144 subdir('swaylock')
145endif
146 156
147config = configuration_data() 157config = configuration_data()
148config.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 bff230f7..72db8ab9 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -110,6 +110,7 @@ static struct cmd_handler handlers[] = {
110 { "seat", cmd_seat }, 110 { "seat", cmd_seat },
111 { "set", cmd_set }, 111 { "set", cmd_set },
112 { "show_marks", cmd_show_marks }, 112 { "show_marks", cmd_show_marks },
113 { "smart_borders", cmd_smart_borders },
113 { "smart_gaps", cmd_smart_gaps }, 114 { "smart_gaps", cmd_smart_gaps },
114 { "tiling_drag", cmd_tiling_drag }, 115 { "tiling_drag", cmd_tiling_drag },
115 { "workspace", cmd_workspace }, 116 { "workspace", cmd_workspace },
@@ -391,14 +392,16 @@ struct cmd_results *config_command(char *exec) {
391 // Var replacement, for all but first argument of set 392 // Var replacement, for all but first argument of set
392 // TODO commands 393 // TODO commands
393 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 394 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
395 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always
396 && handler->handle != cmd_bindsym
397 && handler->handle != cmd_bindcode
398 && handler->handle != cmd_set
399 && (*argv[i] == '\"' || *argv[i] == '\'')) {
400 strip_quotes(argv[i]);
401 }
394 argv[i] = do_var_replacement(argv[i]); 402 argv[i] = do_var_replacement(argv[i]);
395 unescape_string(argv[i]); 403 unescape_string(argv[i]);
396 } 404 }
397 // Strip quotes for first argument.
398 // TODO This part needs to be handled much better
399 if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
400 strip_quotes(argv[1]);
401 }
402 if (handler->handle) { 405 if (handler->handle) {
403 results = handler->handle(argc-1, argv+1); 406 results = handler->handle(argc-1, argv+1);
404 } else { 407 } else {
@@ -422,11 +425,6 @@ struct cmd_results *config_subcommand(char **argv, int argc,
422 char *input = argv[0] ? argv[0] : "(empty)"; 425 char *input = argv[0] ? argv[0] : "(empty)";
423 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); 426 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
424 } 427 }
425 // Strip quotes for first argument.
426 // TODO This part needs to be handled much better
427 if (argc > 1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
428 strip_quotes(argv[1]);
429 }
430 if (handler->handle) { 428 if (handler->handle) {
431 return handler->handle(argc - 1, argv + 1); 429 return handler->handle(argc - 1, argv + 1);
432 } 430 }
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/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/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index 0a5c7f28..ea261fb1 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -1,15 +1,8 @@
1#include "sway/commands.h" 1#include "sway/commands.h"
2#include "sway/config.h" 2#include "sway/config.h"
3#include "sway/tree/container.h" 3#include "sway/tree/arrange.h"
4#include "sway/tree/root.h"
5#include "sway/tree/view.h" 4#include "sway/tree/view.h"
6 5
7static void _configure_view(struct sway_container *con, void *data) {
8 if (con->view) {
9 view_autoconfigure(con->view);
10 }
11}
12
13struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { 6struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
14 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
@@ -26,13 +19,16 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
26 config->hide_edge_borders = E_BOTH; 19 config->hide_edge_borders = E_BOTH;
27 } else if (strcmp(argv[0], "smart") == 0) { 20 } else if (strcmp(argv[0], "smart") == 0) {
28 config->hide_edge_borders = E_SMART; 21 config->hide_edge_borders = E_SMART;
22 } else if (strcmp(argv[0], "smart_no_gaps") == 0) {
23 config->hide_edge_borders = E_SMART_NO_GAPS;
29 } else { 24 } else {
30 return cmd_results_new(CMD_INVALID, "hide_edge_borders", 25 return cmd_results_new(CMD_INVALID, "hide_edge_borders",
31 "Expected 'hide_edge_borders " 26 "Expected 'hide_edge_borders "
32 "<none|vertical|horizontal|both|smart>'"); 27 "<none|vertical|horizontal|both|smart|smart_no_gaps>'");
33 } 28 }
29 config->saved_edge_borders = config->hide_edge_borders;
34 30
35 root_for_each_container(_configure_view, NULL); 31 arrange_root();
36 32
37 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 33 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
38} 34}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index 9091da2a..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 },
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/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/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/smart_borders.c b/sway/commands/smart_borders.c
new file mode 100644
index 00000000..fcb4040e
--- /dev/null
+++ b/sway/commands/smart_borders.c
@@ -0,0 +1,25 @@
1#include "sway/commands.h"
2#include "sway/config.h"
3#include "sway/tree/arrange.h"
4#include "sway/tree/view.h"
5#include "util.h"
6
7struct cmd_results *cmd_smart_borders(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "smart_borders", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 enum edge_border_types saved = config->hide_edge_borders;
14 if (strcmp(argv[0], "no_gaps") == 0) {
15 config->hide_edge_borders = E_SMART_NO_GAPS;
16 } else {
17 config->hide_edge_borders = parse_boolean(argv[0], true) ?
18 E_SMART : config->saved_edge_borders;
19 }
20 config->saved_edge_borders = saved;
21
22 arrange_root();
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25}
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..048b57de 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;
@@ -244,6 +249,7 @@ static void config_defaults(struct sway_config *config) {
244 config->border_thickness = 2; 249 config->border_thickness = 2;
245 config->floating_border_thickness = 2; 250 config->floating_border_thickness = 2;
246 config->hide_edge_borders = E_NONE; 251 config->hide_edge_borders = E_NONE;
252 config->saved_edge_borders = E_NONE;
247 253
248 // border colors 254 // border colors
249 set_color(config->border_colors.focused.border, 0x4C7899); 255 set_color(config->border_colors.focused.border, 0x4C7899);
@@ -804,7 +810,7 @@ char *do_var_replacement(char *str) {
804// would compare two structs in full, while this method only compares the 810// would compare two structs in full, while this method only compares the
805// workspace. 811// workspace.
806int workspace_output_cmp_workspace(const void *a, const void *b) { 812int workspace_output_cmp_workspace(const void *a, const void *b) {
807 const struct workspace_output *wsa = a, *wsb = b; 813 const struct workspace_config *wsa = a, *wsb = b;
808 return lenient_strcmp(wsa->workspace, wsb->workspace); 814 return lenient_strcmp(wsa->workspace, wsb->workspace);
809} 815}
810 816
diff --git a/sway/config/input.c b/sway/config/input.c
index 6b43a5b9..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;
@@ -46,6 +47,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
46 if (src->click_method != INT_MIN) { 47 if (src->click_method != INT_MIN) {
47 dst->click_method = src->click_method; 48 dst->click_method = src->click_method;
48 } 49 }
50 if (src->drag != INT_MIN) {
51 dst->drag = src->drag;
52 }
49 if (src->drag_lock != INT_MIN) { 53 if (src->drag_lock != INT_MIN) {
50 dst->drag_lock = src->drag_lock; 54 dst->drag_lock = src->drag_lock;
51 } 55 }
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/criteria.c b/sway/criteria.c
index 0193233e..575e8bcf 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -339,6 +339,10 @@ static enum criteria_token token_from_name(char *name) {
339 return T_URGENT; 339 return T_URGENT;
340 } else if (strcmp(name, "workspace") == 0) { 340 } else if (strcmp(name, "workspace") == 0) {
341 return T_WORKSPACE; 341 return T_WORKSPACE;
342 } else if (strcmp(name, "tiling") == 0) {
343 return T_TILING;
344 } else if (strcmp(name, "floating") == 0) {
345 return T_FLOATING;
342 } 346 }
343 return T_INVALID; 347 return T_INVALID;
344} 348}
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 4624d824..e717ee35 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -31,14 +31,14 @@ struct sway_transaction_instruction {
31 struct sway_transaction *transaction; 31 struct sway_transaction *transaction;
32 struct sway_node *node; 32 struct sway_node *node;
33 union { 33 union {
34 struct sway_output_state *output_state; 34 struct sway_output_state output_state;
35 struct sway_workspace_state *workspace_state; 35 struct sway_workspace_state workspace_state;
36 struct sway_container_state *container_state; 36 struct sway_container_state container_state;
37 }; 37 };
38 uint32_t serial; 38 uint32_t serial;
39}; 39};
40 40
41static struct sway_transaction *transaction_create() { 41static struct sway_transaction *transaction_create(void) {
42 struct sway_transaction *transaction = 42 struct sway_transaction *transaction =
43 calloc(1, sizeof(struct sway_transaction)); 43 calloc(1, sizeof(struct sway_transaction));
44 if (!sway_assert(transaction, "Unable to allocate transaction")) { 44 if (!sway_assert(transaction, "Unable to allocate transaction")) {
@@ -86,14 +86,7 @@ static void transaction_destroy(struct sway_transaction *transaction) {
86 86
87static void copy_output_state(struct sway_output *output, 87static void copy_output_state(struct sway_output *output,
88 struct sway_transaction_instruction *instruction) { 88 struct sway_transaction_instruction *instruction) {
89 struct sway_output_state *state = 89 struct sway_output_state *state = &instruction->output_state;
90 calloc(1, sizeof(struct sway_output_state));
91 if (!state) {
92 wlr_log(WLR_ERROR, "Could not allocate output state");
93 return;
94 }
95 instruction->output_state = state;
96
97 state->workspaces = create_list(); 90 state->workspaces = create_list();
98 list_cat(state->workspaces, output->workspaces); 91 list_cat(state->workspaces, output->workspaces);
99 92
@@ -102,13 +95,7 @@ static void copy_output_state(struct sway_output *output,
102 95
103static void copy_workspace_state(struct sway_workspace *ws, 96static void copy_workspace_state(struct sway_workspace *ws,
104 struct sway_transaction_instruction *instruction) { 97 struct sway_transaction_instruction *instruction) {
105 struct sway_workspace_state *state = 98 struct sway_workspace_state *state = &instruction->workspace_state;
106 calloc(1, sizeof(struct sway_workspace_state));
107 if (!state) {
108 wlr_log(WLR_ERROR, "Could not allocate workspace state");
109 return;
110 }
111 instruction->workspace_state = state;
112 99
113 state->fullscreen = ws->fullscreen; 100 state->fullscreen = ws->fullscreen;
114 state->x = ws->x; 101 state->x = ws->x;
@@ -138,13 +125,7 @@ static void copy_workspace_state(struct sway_workspace *ws,
138 125
139static void copy_container_state(struct sway_container *container, 126static void copy_container_state(struct sway_container *container,
140 struct sway_transaction_instruction *instruction) { 127 struct sway_transaction_instruction *instruction) {
141 struct sway_container_state *state = 128 struct sway_container_state *state = &instruction->container_state;
142 calloc(1, sizeof(struct sway_container_state));
143 if (!state) {
144 wlr_log(WLR_ERROR, "Could not allocate container state");
145 return;
146 }
147 instruction->container_state = state;
148 129
149 state->layout = container->layout; 130 state->layout = container->layout;
150 state->con_x = container->x; 131 state->con_x = container->x;
@@ -300,15 +281,15 @@ static void transaction_apply(struct sway_transaction *transaction) {
300 case N_ROOT: 281 case N_ROOT:
301 break; 282 break;
302 case N_OUTPUT: 283 case N_OUTPUT:
303 apply_output_state(node->sway_output, instruction->output_state); 284 apply_output_state(node->sway_output, &instruction->output_state);
304 break; 285 break;
305 case N_WORKSPACE: 286 case N_WORKSPACE:
306 apply_workspace_state(node->sway_workspace, 287 apply_workspace_state(node->sway_workspace,
307 instruction->workspace_state); 288 &instruction->workspace_state);
308 break; 289 break;
309 case N_CONTAINER: 290 case N_CONTAINER:
310 apply_container_state(node->sway_container, 291 apply_container_state(node->sway_container,
311 instruction->container_state); 292 &instruction->container_state);
312 break; 293 break;
313 } 294 }
314 295
@@ -334,7 +315,7 @@ static bool transaction_same_nodes(struct sway_transaction *a,
334 return true; 315 return true;
335} 316}
336 317
337static void transaction_progress_queue() { 318static void transaction_progress_queue(void) {
338 if (!server.transactions->length) { 319 if (!server.transactions->length) {
339 return; 320 return;
340 } 321 }
@@ -389,7 +370,7 @@ static bool should_configure(struct sway_node *node,
389 return false; 370 return false;
390 } 371 }
391 struct sway_container_state *cstate = &node->sway_container->current; 372 struct sway_container_state *cstate = &node->sway_container->current;
392 struct sway_container_state *istate = instruction->container_state; 373 struct sway_container_state *istate = &instruction->container_state;
393#ifdef HAVE_XWAYLAND 374#ifdef HAVE_XWAYLAND
394 // Xwayland views are position-aware and need to be reconfigured 375 // Xwayland views are position-aware and need to be reconfigured
395 // when their position changes. 376 // when their position changes.
@@ -417,10 +398,10 @@ static void transaction_commit(struct sway_transaction *transaction) {
417 struct sway_node *node = instruction->node; 398 struct sway_node *node = instruction->node;
418 if (should_configure(node, instruction)) { 399 if (should_configure(node, instruction)) {
419 instruction->serial = view_configure(node->sway_container->view, 400 instruction->serial = view_configure(node->sway_container->view,
420 instruction->container_state->view_x, 401 instruction->container_state.view_x,
421 instruction->container_state->view_y, 402 instruction->container_state.view_y,
422 instruction->container_state->view_width, 403 instruction->container_state.view_width,
423 instruction->container_state->view_height); 404 instruction->container_state.view_height);
424 ++transaction->num_waiting; 405 ++transaction->num_waiting;
425 406
426 // From here on we are rendering a saved buffer of the view, which 407 // From here on we are rendering a saved buffer of the view, which
@@ -512,8 +493,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
512 int width, int height) { 493 int width, int height) {
513 struct sway_transaction_instruction *instruction = 494 struct sway_transaction_instruction *instruction =
514 view->container->node.instruction; 495 view->container->node.instruction;
515 if (instruction->container_state->view_width == width && 496 if (instruction->container_state.view_width == width &&
516 instruction->container_state->view_height == height) { 497 instruction->container_state.view_height == height) {
517 set_instruction_ready(instruction); 498 set_instruction_ready(instruction);
518 } 499 }
519} 500}
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index eab102fd..afad6f6f 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;
@@ -1007,8 +1007,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
1007 if (seat_is_input_allowed(seat, surface)) { 1007 if (seat_is_input_allowed(seat, surface)) {
1008 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, 1008 wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec,
1009 event->touch_id, sx, sy); 1009 event->touch_id, sx, sy);
1010 cursor->image_client = NULL; 1010 cursor_set_image(cursor, NULL, NULL);
1011 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1012 } 1011 }
1013} 1012}
1014 1013
@@ -1176,11 +1175,13 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1176 1175
1177void cursor_set_image(struct sway_cursor *cursor, const char *image, 1176void cursor_set_image(struct sway_cursor *cursor, const char *image,
1178 struct wl_client *client) { 1177 struct wl_client *client) {
1179 if (!cursor->image || strcmp(cursor->image, image) != 0) { 1178 if (!image) {
1179 wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0);
1180 } else if (!cursor->image || strcmp(cursor->image, image) != 0) {
1180 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, 1181 wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image,
1181 cursor->cursor); 1182 cursor->cursor);
1182 cursor->image = image;
1183 } 1183 }
1184 cursor->image = image;
1184 cursor->image_client = client; 1185 cursor->image_client = client;
1185} 1186}
1186 1187
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index f39fe29c..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)",
diff --git a/sway/input/seat.c b/sway/input/seat.c
index a9c564e7..e10b6409 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -144,32 +144,43 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
144 struct sway_node *parent = node_get_parent(node); 144 struct sway_node *parent = node_get_parent(node);
145 struct sway_node *focus = seat_get_focus(seat); 145 struct sway_node *focus = seat_get_focus(seat);
146 146
147 bool set_focus = 147 if (node->type == N_WORKSPACE) {
148 focus != NULL && 148 seat_node_destroy(seat_node);
149 (focus == node || node_has_ancestor(focus, node)) && 149 return;
150 node->type == N_CONTAINER; 150 }
151
152 // Even though the container being destroyed might be nowhere near the
153 // focused container, we still need to set focus_inactive on a sibling of
154 // the container being destroyed.
155 bool needs_new_focus = focus &&
156 (focus == node || node_has_ancestor(focus, node));
151 157
152 seat_node_destroy(seat_node); 158 seat_node_destroy(seat_node);
153 159
154 if (set_focus) { 160 // Find new focus_inactive (ie. sibling, or workspace if no siblings left)
155 struct sway_node *next_focus = NULL; 161 struct sway_node *next_focus = NULL;
156 while (next_focus == NULL) { 162 while (next_focus == NULL) {
157 struct sway_container *con = 163 struct sway_container *con =
158 seat_get_focus_inactive_view(seat, parent); 164 seat_get_focus_inactive_view(seat, parent);
159 next_focus = con ? &con->node : NULL; 165 next_focus = con ? &con->node : NULL;
160 166
161 if (next_focus == NULL && parent->type == N_WORKSPACE) { 167 if (next_focus == NULL && parent->type == N_WORKSPACE) {
162 next_focus = parent; 168 next_focus = parent;
163 break; 169 break;
164 }
165
166 parent = node_get_parent(parent);
167 } 170 }
168 171
169 // the structure change might have caused it to move up to the top of 172 parent = node_get_parent(parent);
173 }
174
175 if (needs_new_focus) {
176 // The structure change might have caused it to move up to the top of
170 // the focus stack without sending focus notifications to the view 177 // the focus stack without sending focus notifications to the view
171 seat_send_focus(next_focus, seat); 178 seat_send_focus(next_focus, seat);
172 seat_set_focus(seat, next_focus); 179 seat_set_focus(seat, next_focus);
180 } else {
181 // Setting focus_inactive
182 seat_set_focus_warp(seat, next_focus, false, false);
183 seat_set_focus_warp(seat, focus, false, false);
173 } 184 }
174} 185}
175 186
@@ -368,11 +379,20 @@ static void seat_update_capabilities(struct sway_seat *seat) {
368 caps |= WL_SEAT_CAPABILITY_TOUCH; 379 caps |= WL_SEAT_CAPABILITY_TOUCH;
369 break; 380 break;
370 case WLR_INPUT_DEVICE_TABLET_TOOL: 381 case WLR_INPUT_DEVICE_TABLET_TOOL:
382 caps |= WL_SEAT_CAPABILITY_POINTER;
383 break;
371 case WLR_INPUT_DEVICE_TABLET_PAD: 384 case WLR_INPUT_DEVICE_TABLET_PAD:
372 break; 385 break;
373 } 386 }
374 } 387 }
375 wlr_seat_set_capabilities(seat->wlr_seat, caps); 388 wlr_seat_set_capabilities(seat->wlr_seat, caps);
389
390 // Hide cursor if seat doesn't have pointer capability
391 if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
392 cursor_set_image(seat->cursor, NULL, NULL);
393 } else {
394 cursor_set_image(seat->cursor, "left_ptr", NULL);
395 }
376} 396}
377 397
378static void seat_apply_input_config(struct sway_seat *seat, 398static void seat_apply_input_config(struct sway_seat *seat,
@@ -552,8 +572,7 @@ void seat_configure_xcursor(struct sway_seat *seat) {
552 output->name, (double)output->scale); 572 output->name, (double)output->scale);
553 } 573 }
554 574
555 wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, 575 cursor_set_image(seat->cursor, "left_ptr", NULL);
556 "left_ptr", seat->cursor->cursor);
557 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, 576 wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
558 seat->cursor->cursor->y); 577 seat->cursor->cursor->y);
559} 578}
@@ -752,6 +771,12 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
752 771
753 seat->has_focus = true; 772 seat->has_focus = true;
754 773
774 if (config->smart_gaps) {
775 // When smart gaps is on, gaps may change when the focus changes so
776 // the workspace needs to be arranged
777 arrange_workspace(new_workspace);
778 }
779
755 update_debug_tree(); 780 update_debug_tree();
756} 781}
757 782
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index df24b812..bc36f9b1 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/main.c b/sway/main.c
index 990f5f3a..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
diff --git a/sway/meson.build b/sway/meson.build
index 2a3c5b5e..00ebcb40 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -79,6 +79,7 @@ sway_sources = files(
79 'commands/seat/fallback.c', 79 'commands/seat/fallback.c',
80 'commands/set.c', 80 'commands/set.c',
81 'commands/show_marks.c', 81 'commands/show_marks.c',
82 'commands/smart_borders.c',
82 'commands/smart_gaps.c', 83 'commands/smart_gaps.c',
83 'commands/split.c', 84 'commands/split.c',
84 'commands/sticky.c', 85 'commands/sticky.c',
@@ -120,6 +121,7 @@ sway_sources = files(
120 121
121 'commands/input/accel_profile.c', 122 'commands/input/accel_profile.c',
122 'commands/input/click_method.c', 123 'commands/input/click_method.c',
124 'commands/input/drag.c',
123 'commands/input/drag_lock.c', 125 'commands/input/drag_lock.c',
124 'commands/input/dwt.c', 126 'commands/input/dwt.c',
125 'commands/input/events.c', 127 'commands/input/events.c',
@@ -165,7 +167,6 @@ sway_deps = [
165 cairo, 167 cairo,
166 gdk_pixbuf, 168 gdk_pixbuf,
167 jsonc, 169 jsonc,
168 libcap,
169 libinput, 170 libinput,
170 math, 171 math,
171 pango, 172 pango,
@@ -188,5 +189,6 @@ executable(
188 include_directories: [sway_inc], 189 include_directories: [sway_inc],
189 dependencies: sway_deps, 190 dependencies: sway_deps,
190 link_with: [lib_sway_common], 191 link_with: [lib_sway_common],
192 install_rpath : rpathdir,
191 install: true 193 install: true
192) 194)
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..3202d517 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.
417 426
418*gaps* inner|outer all|workspace|current set|plus|minus <amount> 427 This affects new workspaces only, and is used when the workspace doesn't
419 Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for 428 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>).
420 all views or workspace, _workspace_ changes gaps for all views in current
421 workspace (or current workspace), and _current_ changes gaps for the current
422 view or workspace.
423 429
424*hide\_edge\_borders* none|vertical|horizontal|both|smart 430*gaps* inner|outer all|current set|plus|minus <amount>
431 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
432 _current_ workspace.
433
434*hide\_edge\_borders* none|vertical|horizontal|both|smart|smart\_no\_gaps
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
@@ -451,6 +456,12 @@ The default colors are:
451*kill* 456*kill*
452 Kills (closes) the currently focused container and all of its children. 457 Kills (closes) the currently focused container and all of its children.
453 458
459*smart\_borders* on|no\_gaps|off
460 If smart\_borders are _on_, borders will only be enabled if the workspace
461 only has one visible child (identical to _hide\_edge\_borders_ smart). If
462 smart\_borders is set to _no\_gaps_, borders will only be enabled if the
463 workspace only has one visible child and gaps greater than zero.
464
454*smart\_gaps* on|off 465*smart\_gaps* on|off
455 If smart\_gaps are _on_ gaps will only be enabled if a workspace has more 466 If smart\_gaps are _on_ gaps will only be enabled if a workspace has more
456 than one child. 467 than one child.
@@ -465,10 +476,8 @@ The default colors are:
465*mode* <mode> 476*mode* <mode>
466 Switches to the specified mode. The default mode _default_. 477 Switches to the specified mode. The default mode _default_.
467 478
468*mode* [--pango\_markup] <mode> *{* <commands...> *}* 479*mode* [--pango\_markup] <mode> <mode-subcommands...>
469 _commands..._ after *{* will be added to the specified mode. A newline is 480 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 481 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango
473 markup. 482 markup.
474 483
@@ -533,8 +542,15 @@ You may combine output commands into one, like so:
533 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch 542 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
534 543
535You can get a list of output names with *swaymsg -t get\_outputs*. You may also 544You 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 545match any output by using the output name "\*".
537config after the others, or it will be matched instead of the others. 546
547*set* $<name> <value>
548 Sets variable $_name_ to _value_. You can use the new variable in the
549 arguments of future commands. When the variable is used, it can be escaped
550 with an additional $ (ie $$_name_) to have the replacement happen at run
551 time instead of when reading the config. However, it does not always make
552 sense for the variable to be replaced at run time since some arguments do
553 need to be known at config time.
538 554
539*show\_marks* yes|no 555*show\_marks* yes|no
540 If *show\_marks* is yes, marks will be displayed in the window borders. 556 If *show\_marks* is yes, marks will be displayed in the window borders.
@@ -568,6 +584,10 @@ config after the others, or it will be matched instead of the others.
568*workspace* back_and_forth 584*workspace* back_and_forth
569 Switches to the previously focused workspace. 585 Switches to the previously focused workspace.
570 586
587*workspace* <name> gaps inner|outer <amount>
588 Specifies that workspace _name_ should have the given gaps settings when it
589 is created.
590
571*workspace* <name> output <output> 591*workspace* <name> output <output>
572 Specifies that workspace _name_ should be shown on the specified _output_. 592 Specifies that workspace _name_ should be shown on the specified _output_.
573 593
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 9b671c1d..a069b177 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -593,7 +593,7 @@ void container_update_representation(struct sway_container *con) {
593 } 593 }
594} 594}
595 595
596size_t container_titlebar_height() { 596size_t container_titlebar_height(void) {
597 return config->font_height + TITLEBAR_V_PADDING * 2; 597 return config->font_height + TITLEBAR_V_PADDING * 2;
598} 598}
599 599
@@ -829,9 +829,16 @@ void container_floating_move_to_center(struct sway_container *con) {
829 return; 829 return;
830 } 830 }
831 struct sway_workspace *ws = con->workspace; 831 struct sway_workspace *ws = con->workspace;
832 bool full = con->is_fullscreen;
833 if (full) {
834 container_set_fullscreen(con, false);
835 }
832 double new_lx = ws->x + (ws->width - con->width) / 2; 836 double new_lx = ws->x + (ws->width - con->width) / 2;
833 double new_ly = ws->y + (ws->height - con->height) / 2; 837 double new_ly = ws->y + (ws->height - con->height) / 2;
834 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 838 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
839 if (full) {
840 container_set_fullscreen(con, true);
841 }
835} 842}
836 843
837static bool find_urgent_iterator(struct sway_container *con, void *data) { 844static bool find_urgent_iterator(struct sway_container *con, void *data) {
@@ -1020,15 +1027,33 @@ void container_add_gaps(struct sway_container *c) {
1020 if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { 1027 if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) {
1021 return; 1028 return;
1022 } 1029 }
1023 // Children of tabbed/stacked containers re-use the gaps of the container 1030 // Descendants of tabbed/stacked containers re-use the gaps of the container
1024 enum sway_container_layout layout = container_parent_layout(c); 1031 struct sway_container *temp = c;
1025 if (layout == L_TABBED || layout == L_STACKED) { 1032 while (temp) {
1026 return; 1033 enum sway_container_layout layout = container_parent_layout(temp);
1034 if (layout == L_TABBED || layout == L_STACKED) {
1035 return;
1036 }
1037 temp = temp->parent;
1038 }
1039 // If smart gaps is on, don't add gaps if there is only one view visible
1040 if (config->smart_gaps) {
1041 struct sway_view *view = c->view;
1042 if (!view) {
1043 struct sway_seat *seat =
1044 input_manager_get_default_seat(input_manager);
1045 struct sway_container *focus =
1046 seat_get_focus_inactive_view(seat, &c->node);
1047 view = focus ? focus->view : NULL;
1048 }
1049 if (view && view_is_only_visible(view)) {
1050 return;
1051 }
1027 } 1052 }
1028 1053
1029 struct sway_workspace *ws = c->workspace; 1054 struct sway_workspace *ws = c->workspace;
1030 1055
1031 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1056 c->current_gaps = ws->gaps_inner;
1032 c->x += c->current_gaps; 1057 c->x += c->current_gaps;
1033 c->y += c->current_gaps; 1058 c->y += c->current_gaps;
1034 c->width -= 2 * c->current_gaps; 1059 c->width -= 2 * c->current_gaps;
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 06933dc4..c3176325 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -113,6 +113,20 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
113 arrange_root(); 113 arrange_root();
114} 114}
115 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
116static void output_evacuate(struct sway_output *output) { 130static void output_evacuate(struct sway_output *output) {
117 if (!output->workspaces->length) { 131 if (!output->workspaces->length) {
118 return; 132 return;
@@ -130,17 +144,21 @@ static void output_evacuate(struct sway_output *output) {
130 144
131 workspace_detach(workspace); 145 workspace_detach(workspace);
132 146
133 if (workspace_is_empty(workspace)) {
134 workspace_begin_destroy(workspace);
135 continue;
136 }
137
138 struct sway_output *new_output = 147 struct sway_output *new_output =
139 workspace_output_get_highest_available(workspace, output); 148 workspace_output_get_highest_available(workspace, output);
140 if (!new_output) { 149 if (!new_output) {
141 new_output = fallback_output; 150 new_output = fallback_output;
142 } 151 }
143 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
144 if (new_output) { 162 if (new_output) {
145 workspace_output_add_priority(workspace, new_output); 163 workspace_output_add_priority(workspace, new_output);
146 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 d6f67bd7..6748e9c9 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -273,6 +273,12 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *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(
@@ -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 9ffcf206..9c7c44e9 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -165,6 +165,34 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
165 return 0; 165 return 0;
166} 166}
167 167
168bool view_is_only_visible(struct sway_view *view) {
169 bool only_view = true;
170 struct sway_container *con = view->container;
171 while (con) {
172 enum sway_container_layout layout = container_parent_layout(con);
173 if (layout != L_TABBED && layout != L_STACKED) {
174 list_t *siblings = container_get_siblings(con);
175 if (siblings && siblings->length > 1) {
176 only_view = false;
177 break;
178 }
179 }
180 con = con->parent;
181 }
182 return only_view;
183}
184
185static bool gaps_to_edge(struct sway_view *view) {
186 struct sway_container *con = view->container;
187 while (con) {
188 if (con->current_gaps > 0) {
189 return true;
190 }
191 con = con->parent;
192 }
193 return view->container->workspace->current_gaps > 0;
194}
195
168void view_autoconfigure(struct sway_view *view) { 196void view_autoconfigure(struct sway_view *view) {
169 if (!view->container->workspace) { 197 if (!view->container->workspace) {
170 // Hidden in the scratchpad 198 // Hidden in the scratchpad
@@ -181,39 +209,28 @@ void view_autoconfigure(struct sway_view *view) {
181 } 209 }
182 210
183 struct sway_workspace *ws = view->container->workspace; 211 struct sway_workspace *ws = view->container->workspace;
184
185 bool other_views = false;
186 if (config->hide_edge_borders == E_SMART) {
187 struct sway_container *con = view->container;
188 while (con) {
189 enum sway_container_layout layout = container_parent_layout(con);
190 if (layout != L_TABBED && layout != L_STACKED) {
191 list_t *siblings = container_get_siblings(con);
192 if (siblings && siblings->length > 1) {
193 other_views = true;
194 break;
195 }
196 }
197 con = con->parent;
198 }
199 }
200
201 struct sway_container *con = view->container; 212 struct sway_container *con = view->container;
202 213
214 bool smart = config->hide_edge_borders == E_SMART ||
215 config->hide_edge_borders == E_SMART_NO_GAPS;
216 bool other_views = smart && !view_is_only_visible(view);
217 bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS
218 || !gaps_to_edge(view);
219
203 view->border_top = view->border_bottom = true; 220 view->border_top = view->border_bottom = true;
204 view->border_left = view->border_right = true; 221 view->border_left = view->border_right = true;
205 if (config->hide_edge_borders == E_BOTH 222 if (config->hide_edge_borders == E_BOTH
206 || config->hide_edge_borders == E_VERTICAL 223 || config->hide_edge_borders == E_VERTICAL
207 || (config->hide_edge_borders == E_SMART && !other_views)) { 224 || (smart && !other_views && no_gaps)) {
208 view->border_left = con->x != ws->x; 225 view->border_left = con->x - con->current_gaps != ws->x;
209 int right_x = con->x + con->width; 226 int right_x = con->x + con->width + con->current_gaps;
210 view->border_right = right_x != ws->x + ws->width; 227 view->border_right = right_x != ws->x + ws->width;
211 } 228 }
212 if (config->hide_edge_borders == E_BOTH 229 if (config->hide_edge_borders == E_BOTH
213 || config->hide_edge_borders == E_HORIZONTAL 230 || config->hide_edge_borders == E_HORIZONTAL
214 || (config->hide_edge_borders == E_SMART && !other_views)) { 231 || (smart && !other_views && no_gaps)) {
215 view->border_top = con->y != ws->y; 232 view->border_top = con->y - con->current_gaps != ws->y;
216 int bottom_y = con->y + con->height; 233 int bottom_y = con->y + con->height + con->current_gaps;
217 view->border_bottom = bottom_y != ws->y + ws->height; 234 view->border_bottom = bottom_y != ws->y + ws->height;
218 } 235 }
219 236
@@ -1004,12 +1021,16 @@ bool view_is_visible(struct sway_view *view) {
1004 floater = floater->parent; 1021 floater = floater->parent;
1005 } 1022 }
1006 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 1023 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
1024 if (!is_sticky && !workspace_is_visible(workspace)) {
1025 return false;
1026 }
1007 // Check view isn't in a tabbed or stacked container on an inactive tab 1027 // Check view isn't in a tabbed or stacked container on an inactive tab
1008 struct sway_seat *seat = input_manager_current_seat(input_manager); 1028 struct sway_seat *seat = input_manager_current_seat(input_manager);
1009 struct sway_container *con = view->container; 1029 struct sway_container *con = view->container;
1010 while (con) { 1030 while (con) {
1011 enum sway_container_layout layout = container_parent_layout(con); 1031 enum sway_container_layout layout = container_parent_layout(con);
1012 if (layout == L_TABBED || layout == L_STACKED) { 1032 if ((layout == L_TABBED || layout == L_STACKED)
1033 && !container_is_floating(con)) {
1013 struct sway_node *parent = con->parent ? 1034 struct sway_node *parent = con->parent ?
1014 &con->parent->node : &con->workspace->node; 1035 &con->parent->node : &con->workspace->node;
1015 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1036 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
@@ -1023,10 +1044,6 @@ bool view_is_visible(struct sway_view *view) {
1023 !container_is_fullscreen_or_child(view->container)) { 1044 !container_is_fullscreen_or_child(view->container)) {
1024 return false; 1045 return false;
1025 } 1046 }
1026 // Check the workspace is visible
1027 if (!is_sticky) {
1028 return workspace_is_visible(workspace);
1029 }
1030 return true; 1047 return true;
1031} 1048}
1032 1049
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 16031e87..e9e5dfa2 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
@@ -62,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output,
62 ws->output_priority = create_list(); 68 ws->output_priority = create_list();
63 workspace_output_add_priority(ws, output); 69 workspace_output_add_priority(ws, output);
64 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
65 output_add_workspace(output, ws); 85 output_add_workspace(output, ws);
66 output_sort_workspaces(output); 86 output_sort_workspaces(output);
67 87
@@ -121,17 +141,8 @@ void next_name_map(struct sway_container *ws, void *data) {
121 141
122static bool workspace_valid_on_output(const char *output_name, 142static bool workspace_valid_on_output(const char *output_name,
123 const char *ws_name) { 143 const char *ws_name) {
124 int i; 144 struct workspace_config *wsc = workspace_find_config(ws_name);
125 for (i = 0; i < config->workspace_outputs->length; ++i) { 145 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0;
126 struct workspace_output *wso = config->workspace_outputs->items[i];
127 if (strcasecmp(wso->workspace, ws_name) == 0) {
128 if (strcasecmp(wso->output, output_name) != 0) {
129 return false;
130 }
131 }
132 }
133
134 return true;
135} 146}
136 147
137static void workspace_name_from_binding(const struct sway_binding * binding, 148static void workspace_name_from_binding(const struct sway_binding * binding,
@@ -231,13 +242,13 @@ char *workspace_next_name(const char *output_name) {
231 workspace_name_from_binding(mode->keycode_bindings->items[i], 242 workspace_name_from_binding(mode->keycode_bindings->items[i],
232 output_name, &order, &target); 243 output_name, &order, &target);
233 } 244 }
234 for (int i = 0; i < config->workspace_outputs->length; ++i) { 245 for (int i = 0; i < config->workspace_configs->length; ++i) {
235 // Unlike with bindings, this does not guarantee order 246 // Unlike with bindings, this does not guarantee order
236 const struct workspace_output *wso = config->workspace_outputs->items[i]; 247 const struct workspace_config *wsc = config->workspace_configs->items[i];
237 if (strcmp(wso->output, output_name) == 0 248 if (wsc->output && strcmp(wsc->output, output_name) == 0
238 && workspace_by_name(wso->workspace) == NULL) { 249 && workspace_by_name(wsc->workspace) == NULL) {
239 free(target); 250 free(target);
240 target = strdup(wso->workspace); 251 target = strdup(wsc->workspace);
241 break; 252 break;
242 } 253 }
243 } 254 }
@@ -629,19 +640,25 @@ void workspace_add_gaps(struct sway_workspace *ws) {
629 if (ws->current_gaps > 0) { 640 if (ws->current_gaps > 0) {
630 return; 641 return;
631 } 642 }
632 bool should_apply = 643 if (!config->edge_gaps) {
633 config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1);
634 if (!should_apply) {
635 return; 644 return;
636 } 645 }
646 if (config->smart_gaps) {
647 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
648 struct sway_container *focus =
649 seat_get_focus_inactive_view(seat, &ws->node);
650 if (focus && focus->view && view_is_only_visible(focus->view)) {
651 return;
652 }
653 }
637 654
638 ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; 655 ws->current_gaps = ws->gaps_outer;
639 656
640 if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 657 if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
641 // We have to add inner gaps for this, because children of tabbed and 658 // We have to add inner gaps for this, because children of tabbed and
642 // stacked containers don't apply their own gaps - they assume the 659 // stacked containers don't apply their own gaps - they assume the
643 // tabbed/stacked container is using gaps. 660 // tabbed/stacked container is using gaps.
644 ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 661 ws->current_gaps += ws->gaps_inner;
645 } 662 }
646 663
647 ws->x += ws->current_gaps; 664 ws->x += ws->current_gaps;
diff --git a/swaybar/bar.c b/swaybar/bar.c
index ab307fd4..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"
@@ -71,6 +72,16 @@ static void swaybar_output_free(struct swaybar_output *output) {
71 free(output); 72 free(output);
72} 73}
73 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
74static void layer_surface_configure(void *data, 85static void layer_surface_configure(void *data,
75 struct zwlr_layer_surface_v1 *surface, 86 struct zwlr_layer_surface_v1 *surface,
76 uint32_t serial, uint32_t width, uint32_t height) { 87 uint32_t serial, uint32_t width, uint32_t height) {
@@ -78,7 +89,7 @@ static void layer_surface_configure(void *data,
78 output->width = width; 89 output->width = width;
79 output->height = height; 90 output->height = height;
80 zwlr_layer_surface_v1_ack_configure(surface, serial); 91 zwlr_layer_surface_v1_ack_configure(surface, serial);
81 render_frame(output->bar, output); 92 set_output_dirty(output);
82} 93}
83 94
84static void layer_surface_closed(void *_output, 95static void layer_surface_closed(void *_output,
@@ -324,27 +335,22 @@ static void output_geometry(void *data, struct wl_output *wl_output, int32_t x,
324 const char *make, const char *model, int32_t transform) { 335 const char *make, const char *model, int32_t transform) {
325 struct swaybar_output *output = data; 336 struct swaybar_output *output = data;
326 output->subpixel = subpixel; 337 output->subpixel = subpixel;
327 if (output->surface) {
328 render_frame(output->bar, output);
329 }
330} 338}
331 339
332static 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,
333 int32_t width, int32_t height, int32_t refresh) { 341 int32_t width, int32_t height, int32_t refresh) {
334 // Who cares 342 // Who cares
335} 343}
336 344
337static void output_done(void *data, struct wl_output *output) { 345static void output_done(void *data, struct wl_output *wl_output) {
338 // Who cares 346 struct swaybar_output *output = data;
347 set_output_dirty(output);
339} 348}
340 349
341static void output_scale(void *data, struct wl_output *wl_output, 350static void output_scale(void *data, struct wl_output *wl_output,
342 int32_t factor) { 351 int32_t factor) {
343 struct swaybar_output *output = data; 352 struct swaybar_output *output = data;
344 output->scale = factor; 353 output->scale = factor;
345 if (output->surface) {
346 render_frame(output->bar, output);
347 }
348} 354}
349 355
350struct wl_output_listener output_listener = { 356struct wl_output_listener output_listener = {
@@ -380,7 +386,7 @@ static void xdg_output_handle_done(void *data,
380 wl_list_insert(&bar->outputs, &output->link); 386 wl_list_insert(&bar->outputs, &output->link);
381 387
382 add_layer_surface(output); 388 add_layer_surface(output);
383 render_frame(bar, output); 389 set_output_dirty(output);
384 } 390 }
385} 391}
386 392
@@ -469,23 +475,23 @@ static const struct wl_registry_listener registry_listener = {
469 .global_remove = handle_global_remove, 475 .global_remove = handle_global_remove,
470}; 476};
471 477
472static void render_all_frames(struct swaybar *bar) { 478static void set_bar_dirty(struct swaybar *bar) {
473 struct swaybar_output *output; 479 struct swaybar_output *output;
474 wl_list_for_each(output, &bar->outputs, link) { 480 wl_list_for_each(output, &bar->outputs, link) {
475 if (output->surface != NULL) { 481 set_output_dirty(output);
476 render_frame(bar, output);
477 }
478 } 482 }
479} 483}
480 484
481void bar_setup(struct swaybar *bar, 485bool bar_setup(struct swaybar *bar,
482 const char *socket_path, const char *bar_id) { 486 const char *socket_path, const char *bar_id) {
483 bar_init(bar); 487 bar_init(bar);
484 init_event_loop(); 488 init_event_loop();
485 489
486 bar->ipc_socketfd = ipc_open_socket(socket_path); 490 bar->ipc_socketfd = ipc_open_socket(socket_path);
487 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 491 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
488 ipc_initialize(bar, bar_id); 492 if (!ipc_initialize(bar, bar_id)) {
493 return false;
494 }
489 if (bar->config->status_command) { 495 if (bar->config->status_command) {
490 bar->status = status_line_init(bar->config->status_command); 496 bar->status = status_line_init(bar->config->status_command);
491 } 497 }
@@ -525,7 +531,8 @@ void bar_setup(struct swaybar *bar,
525 assert(pointer->cursor_surface); 531 assert(pointer->cursor_surface);
526 532
527 ipc_get_workspaces(bar); 533 ipc_get_workspaces(bar);
528 render_all_frames(bar); 534 set_bar_dirty(bar);
535 return true;
529} 536}
530 537
531static void display_in(int fd, short mask, void *data) { 538static void display_in(int fd, short mask, void *data) {
@@ -539,7 +546,7 @@ static void display_in(int fd, short mask, void *data) {
539static void ipc_in(int fd, short mask, void *data) { 546static void ipc_in(int fd, short mask, void *data) {
540 struct swaybar *bar = data; 547 struct swaybar *bar = data;
541 if (handle_ipc_readable(bar)) { 548 if (handle_ipc_readable(bar)) {
542 render_all_frames(bar); 549 set_bar_dirty(bar);
543 } 550 }
544} 551}
545 552
@@ -547,10 +554,10 @@ static void status_in(int fd, short mask, void *data) {
547 struct swaybar *bar = data; 554 struct swaybar *bar = data;
548 if (mask & (POLLHUP | POLLERR)) { 555 if (mask & (POLLHUP | POLLERR)) {
549 status_error(bar->status, "[error reading from status command]"); 556 status_error(bar->status, "[error reading from status command]");
550 render_all_frames(bar); 557 set_bar_dirty(bar);
551 remove_event(fd); 558 remove_event(fd);
552 } else if (status_handle_readable(bar->status)) { 559 } else if (status_handle_readable(bar->status)) {
553 render_all_frames(bar); 560 set_bar_dirty(bar);
554 } 561 }
555} 562}
556 563
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 325aa61a..1d754808 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) {
@@ -258,7 +260,7 @@ bool i3bar_handle_readable(struct status_line *status) {
258 260
259enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 261enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
260 struct i3bar_block *block, int x, int y, enum x11_button button) { 262 struct i3bar_block *block, int x, int y, enum x11_button button) {
261 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); 263 wlr_log(WLR_DEBUG, "block %s clicked", block->name);
262 if (!block->name || !status->click_events) { 264 if (!block->name || !status->click_events) {
263 return HOTSPOT_PROCESS; 265 return HOTSPOT_PROCESS;
264 } 266 }
@@ -274,10 +276,11 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
274 json_object_object_add(event_json, "button", json_object_new_int(button)); 276 json_object_object_add(event_json, "button", json_object_new_int(button));
275 json_object_object_add(event_json, "x", json_object_new_int(x)); 277 json_object_object_add(event_json, "x", json_object_new_int(x));
276 json_object_object_add(event_json, "y", json_object_new_int(y)); 278 json_object_object_add(event_json, "y", json_object_new_int(y));
277 if (dprintf(status->write_fd, "%s,\n", 279 if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "",
278 json_object_to_json_string(event_json)) < 0) { 280 json_object_to_json_string(event_json)) < 0) {
279 status_error(status, "[failed to write click event]"); 281 status_error(status, "[failed to write click event]");
280 } 282 }
283 status->clicked = true;
281 json_object_put(event_json); 284 json_object_put(event_json);
282 return HOTSPOT_IGNORE; 285 return HOTSPOT_IGNORE;
283} 286}
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 9413dc57..dc31a5ea 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -10,6 +10,7 @@
10#include "pool-buffer.h" 10#include "pool-buffer.h"
11#include "swaybar/bar.h" 11#include "swaybar/bar.h"
12#include "swaybar/config.h" 12#include "swaybar/config.h"
13#include "swaybar/i3bar.h"
13#include "swaybar/ipc.h" 14#include "swaybar/ipc.h"
14#include "swaybar/render.h" 15#include "swaybar/render.h"
15#include "swaybar/status_line.h" 16#include "swaybar/status_line.h"
@@ -20,47 +21,47 @@ static const double WS_VERTICAL_PADDING = 1.5;
20static const double BORDER_WIDTH = 1; 21static const double BORDER_WIDTH = 1;
21 22
22static uint32_t render_status_line_error(cairo_t *cairo, 23static uint32_t render_status_line_error(cairo_t *cairo,
23 struct swaybar_output *output, struct swaybar_config *config, 24 struct swaybar_output *output, double *x) {
24 const char *error, double *x, uint32_t surface_height) { 25 const char *error = output->bar->status->text;
25 if (!error) { 26 if (!error) {
26 return 0; 27 return 0;
27 } 28 }
28 29
29 uint32_t height = surface_height * output->scale; 30 uint32_t height = output->height * output->scale;
30 31
31 cairo_set_source_u32(cairo, 0xFF0000FF); 32 cairo_set_source_u32(cairo, 0xFF0000FF);
32 33
33 int margin = 3 * output->scale; 34 int margin = 3 * output->scale;
34 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 35 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale;
35 36
37 char *font = output->bar->config->font;
36 int text_width, text_height; 38 int text_width, text_height;
37 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 39 get_text_size(cairo, font, &text_width, &text_height, NULL,
38 output->scale, false, "%s", error); 40 output->scale, false, "%s", error);
39 41
40 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 42 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
41 uint32_t ideal_surface_height = ideal_height / output->scale; 43 uint32_t ideal_surface_height = ideal_height / output->scale;
42 if (surface_height < ideal_surface_height) { 44 if (output->height < ideal_surface_height) {
43 return ideal_surface_height; 45 return ideal_surface_height;
44 } 46 }
45 *x -= text_width + margin; 47 *x -= text_width + margin;
46 48
47 double text_y = height / 2.0 - text_height / 2.0; 49 double text_y = height / 2.0 - text_height / 2.0;
48 cairo_move_to(cairo, *x, (int)floor(text_y)); 50 cairo_move_to(cairo, *x, (int)floor(text_y));
49 pango_printf(cairo, config->font, output->scale, false, "%s", error); 51 pango_printf(cairo, font, output->scale, false, "%s", error);
50 *x -= margin; 52 *x -= margin;
51 return surface_height; 53 return output->height;
52} 54}
53 55
54static uint32_t render_status_line_text(cairo_t *cairo, 56static uint32_t render_status_line_text(cairo_t *cairo,
55 struct swaybar_output *output, struct swaybar_config *config, 57 struct swaybar_output *output, double *x) {
56 const char *text, bool focused, double *x, uint32_t surface_height) { 58 const char *text = output->bar->status->text;
57 if (!text) { 59 if (!text) {
58 return 0; 60 return 0;
59 } 61 }
60 62
61 uint32_t height = surface_height * output->scale; 63 struct swaybar_config *config = output->bar->config;
62 64 cairo_set_source_u32(cairo, output->focused ?
63 cairo_set_source_u32(cairo, focused ?
64 config->colors.focused_statusline : config->colors.statusline); 65 config->colors.focused_statusline : config->colors.statusline);
65 66
66 int text_width, text_height; 67 int text_width, text_height;
@@ -72,17 +73,18 @@ static uint32_t render_status_line_text(cairo_t *cairo,
72 73
73 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 74 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
74 uint32_t ideal_surface_height = ideal_height / output->scale; 75 uint32_t ideal_surface_height = ideal_height / output->scale;
75 if (surface_height < ideal_surface_height) { 76 if (output->height < ideal_surface_height) {
76 return ideal_surface_height; 77 return ideal_surface_height;
77 } 78 }
78 79
79 *x -= text_width + margin; 80 *x -= text_width + margin;
81 uint32_t height = output->height * output->scale;
80 double text_y = height / 2.0 - text_height / 2.0; 82 double text_y = height / 2.0 - text_height / 2.0;
81 cairo_move_to(cairo, *x, (int)floor(text_y)); 83 cairo_move_to(cairo, *x, (int)floor(text_y));
82 pango_printf(cairo, config->font, output->scale, 84 pango_printf(cairo, config->font, output->scale,
83 config->pango_markup, "%s", text); 85 config->pango_markup, "%s", text);
84 *x -= margin; 86 *x -= margin;
85 return surface_height; 87 return output->height;
86} 88}
87 89
88static void render_sharp_line(cairo_t *cairo, uint32_t color, 90static void render_sharp_line(cairo_t *cairo, uint32_t color,
@@ -122,12 +124,11 @@ static void i3bar_block_unref_callback(void *data) {
122 124
123static uint32_t render_status_block(cairo_t *cairo, 125static uint32_t render_status_block(cairo_t *cairo,
124 struct swaybar_output *output, struct i3bar_block *block, double *x, 126 struct swaybar_output *output, struct i3bar_block *block, double *x,
125 uint32_t surface_height, bool focused, bool edge) { 127 bool edge) {
126 if (!block->full_text || !*block->full_text) { 128 if (!block->full_text || !*block->full_text) {
127 return 0; 129 return 0;
128 } 130 }
129 131
130 uint32_t height = surface_height * output->scale;
131 struct swaybar_config *config = output->bar->config; 132 struct swaybar_config *config = output->bar->config;
132 133
133 int text_width, text_height; 134 int text_width, text_height;
@@ -145,7 +146,7 @@ static uint32_t render_status_block(cairo_t *cairo,
145 double block_width = width; 146 double block_width = width;
146 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 147 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
147 uint32_t ideal_surface_height = ideal_height / output->scale; 148 uint32_t ideal_surface_height = ideal_height / output->scale;
148 if (surface_height < ideal_surface_height) { 149 if (output->height < ideal_surface_height) {
149 return ideal_surface_height; 150 return ideal_surface_height;
150 } 151 }
151 152
@@ -166,7 +167,7 @@ static uint32_t render_status_block(cairo_t *cairo,
166 output->scale, false, "%s", config->sep_symbol); 167 output->scale, false, "%s", config->sep_symbol);
167 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 168 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
168 uint32_t _ideal_surface_height = _ideal_height / output->scale; 169 uint32_t _ideal_surface_height = _ideal_height / output->scale;
169 if (surface_height < _ideal_surface_height) { 170 if (output->height < _ideal_surface_height) {
170 return _ideal_surface_height; 171 return _ideal_surface_height;
171 } 172 }
172 if (sep_width > block->separator_block_width) { 173 if (sep_width > block->separator_block_width) {
@@ -178,6 +179,7 @@ static uint32_t render_status_block(cairo_t *cairo,
178 *x -= margin; 179 *x -= margin;
179 } 180 }
180 181
182 uint32_t height = output->height * output->scale;
181 if (output->bar->status->click_events) { 183 if (output->bar->status->click_events) {
182 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 184 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
183 hotspot->x = *x; 185 hotspot->x = *x;
@@ -241,7 +243,7 @@ static uint32_t render_status_block(cairo_t *cairo,
241 } 243 }
242 244
243 if (!edge && block->separator) { 245 if (!edge && block->separator) {
244 if (focused) { 246 if (output->focused) {
245 cairo_set_source_u32(cairo, config->colors.focused_separator); 247 cairo_set_source_u32(cairo, config->colors.focused_separator);
246 } else { 248 } else {
247 cairo_set_source_u32(cairo, config->colors.separator); 249 cairo_set_source_u32(cairo, config->colors.separator);
@@ -260,19 +262,16 @@ static uint32_t render_status_block(cairo_t *cairo,
260 cairo_stroke(cairo); 262 cairo_stroke(cairo);
261 } 263 }
262 } 264 }
263 return surface_height; 265 return output->height;
264} 266}
265 267
266static uint32_t render_status_line_i3bar(cairo_t *cairo, 268static uint32_t render_status_line_i3bar(cairo_t *cairo,
267 struct swaybar_config *config, struct swaybar_output *output, 269 struct swaybar_output *output, double *x) {
268 struct status_line *status, bool focused,
269 double *x, uint32_t surface_height) {
270 uint32_t max_height = 0; 270 uint32_t max_height = 0;
271 bool edge = true; 271 bool edge = true;
272 struct i3bar_block *block; 272 struct i3bar_block *block;
273 wl_list_for_each(block, &status->blocks, link) { 273 wl_list_for_each(block, &output->bar->status->blocks, link) {
274 uint32_t h = render_status_block(cairo, output, 274 uint32_t h = render_status_block(cairo, output, block, x, edge);
275 block, x, surface_height, focused, edge);
276 max_height = h > max_height ? h : max_height; 275 max_height = h > max_height ? h : max_height;
277 edge = false; 276 edge = false;
278 } 277 }
@@ -280,19 +279,15 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
280} 279}
281 280
282static uint32_t render_status_line(cairo_t *cairo, 281static uint32_t render_status_line(cairo_t *cairo,
283 struct swaybar_config *config, struct swaybar_output *output, 282 struct swaybar_output *output, double *x) {
284 struct status_line *status, bool focused, 283 struct status_line *status = output->bar->status;
285 double *x, uint32_t surface_height) {
286 switch (status->protocol) { 284 switch (status->protocol) {
287 case PROTOCOL_ERROR: 285 case PROTOCOL_ERROR:
288 return render_status_line_error(cairo, output, config, 286 return render_status_line_error(cairo, output, x);
289 status->text, x, surface_height);
290 case PROTOCOL_TEXT: 287 case PROTOCOL_TEXT:
291 return render_status_line_text(cairo, output, config, 288 return render_status_line_text(cairo, output, x);
292 status->text, focused, x, surface_height);
293 case PROTOCOL_I3BAR: 289 case PROTOCOL_I3BAR:
294 return render_status_line_i3bar(cairo, config, output, 290 return render_status_line_i3bar(cairo, output, x);
295 status, focused, x, surface_height);
296 case PROTOCOL_UNDEF: 291 case PROTOCOL_UNDEF:
297 return 0; 292 return 0;
298 } 293 }
@@ -300,10 +295,9 @@ static uint32_t render_status_line(cairo_t *cairo,
300} 295}
301 296
302static uint32_t render_binding_mode_indicator(cairo_t *cairo, 297static uint32_t render_binding_mode_indicator(cairo_t *cairo,
303 struct swaybar_output *output, struct swaybar_config *config, 298 struct swaybar_output *output, double x) {
304 const char *mode, double x, uint32_t surface_height) { 299 struct swaybar_config *config = output->bar->config;
305 uint32_t height = surface_height * output->scale; 300 const char *mode = config->mode;
306
307 int text_width, text_height; 301 int text_width, text_height;
308 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 302 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
309 output->scale, config->mode_pango_markup, 303 output->scale, config->mode_pango_markup,
@@ -316,11 +310,12 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
316 uint32_t ideal_height = text_height + ws_vertical_padding * 2 310 uint32_t ideal_height = text_height + ws_vertical_padding * 2
317 + border_width * 2; 311 + border_width * 2;
318 uint32_t ideal_surface_height = ideal_height / output->scale; 312 uint32_t ideal_surface_height = ideal_height / output->scale;
319 if (surface_height < ideal_surface_height) { 313 if (output->height < ideal_surface_height) {
320 return ideal_surface_height; 314 return ideal_surface_height;
321 } 315 }
322 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;
323 317
318 uint32_t height = output->height * output->scale;
324 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 319 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
325 cairo_rectangle(cairo, x, 0, width, height); 320 cairo_rectangle(cairo, x, 0, width, height);
326 cairo_fill(cairo); 321 cairo_fill(cairo);
@@ -340,7 +335,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
340 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));
341 pango_printf(cairo, config->font, output->scale, config->mode_pango_markup, 336 pango_printf(cairo, config->font, output->scale, config->mode_pango_markup,
342 "%s", mode); 337 "%s", mode);
343 return surface_height; 338 return output->height;
344} 339}
345 340
346static const char *strip_workspace_number(const char *ws_name) { 341static const char *strip_workspace_number(const char *ws_name) {
@@ -366,8 +361,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_out
366} 361}
367 362
368static uint32_t render_workspace_button(cairo_t *cairo, 363static uint32_t render_workspace_button(cairo_t *cairo,
369 struct swaybar_output *output, struct swaybar_config *config, 364 struct swaybar_output *output,
370 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;
371 const char *name = ws->name; 367 const char *name = ws->name;
372 if (config->strip_workspace_numbers) { 368 if (config->strip_workspace_numbers) {
373 name = strip_workspace_number(ws->name); 369 name = strip_workspace_number(ws->name);
@@ -384,7 +380,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
384 box_colors = config->colors.inactive_workspace; 380 box_colors = config->colors.inactive_workspace;
385 } 381 }
386 382
387 uint32_t height = surface_height * output->scale; 383 uint32_t height = output->height * output->scale;
388 384
389 int text_width, text_height; 385 int text_width, text_height;
390 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 386 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -397,7 +393,7 @@ static uint32_t render_workspace_button(cairo_t *cairo,
397 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 393 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
398 + border_width * 2; 394 + border_width * 2;
399 uint32_t ideal_surface_height = ideal_height / output->scale; 395 uint32_t ideal_surface_height = ideal_height / output->scale;
400 if (surface_height < ideal_surface_height) { 396 if (output->height < ideal_surface_height) {
401 return ideal_surface_height; 397 return ideal_surface_height;
402 } 398 }
403 399
@@ -434,11 +430,11 @@ static uint32_t render_workspace_button(cairo_t *cairo,
434 wl_list_insert(&output->hotspots, &hotspot->link); 430 wl_list_insert(&output->hotspots, &hotspot->link);
435 431
436 *x += width; 432 *x += width;
437 return surface_height; 433 return output->height;
438} 434}
439 435
440static uint32_t render_to_cairo(cairo_t *cairo, 436static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
441 struct swaybar *bar, struct swaybar_output *output) { 437 struct swaybar *bar = output->bar;
442 struct swaybar_config *config = bar->config; 438 struct swaybar_config *config = bar->config;
443 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 439 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
444 if (output->focused) { 440 if (output->focused) {
@@ -458,29 +454,41 @@ static uint32_t render_to_cairo(cairo_t *cairo,
458 */ 454 */
459 double x = output->width * output->scale; 455 double x = output->width * output->scale;
460 if (bar->status) { 456 if (bar->status) {
461 uint32_t h = render_status_line(cairo, config, output, 457 uint32_t h = render_status_line(cairo, output, &x);
462 bar->status, output->focused, &x, output->height);
463 max_height = h > max_height ? h : max_height; 458 max_height = h > max_height ? h : max_height;
464 } 459 }
465 x = 0; 460 x = 0;
466 if (config->workspace_buttons) { 461 if (config->workspace_buttons) {
467 struct swaybar_workspace *ws; 462 struct swaybar_workspace *ws;
468 wl_list_for_each_reverse(ws, &output->workspaces, link) { 463 wl_list_for_each_reverse(ws, &output->workspaces, link) {
469 uint32_t h = render_workspace_button(cairo, 464 uint32_t h = render_workspace_button(cairo, output, ws, &x);
470 output, config, ws, &x, output->height);
471 max_height = h > max_height ? h : max_height; 465 max_height = h > max_height ? h : max_height;
472 } 466 }
473 } 467 }
474 if (config->binding_mode_indicator && config->mode) { 468 if (config->binding_mode_indicator && config->mode) {
475 uint32_t h = render_binding_mode_indicator(cairo, 469 uint32_t h = render_binding_mode_indicator(cairo, output, x);
476 output, config, config->mode, x, output->height);
477 max_height = h > max_height ? h : max_height; 470 max_height = h > max_height ? h : max_height;
478 } 471 }
479 472
480 return max_height > output->height ? max_height : output->height; 473 return max_height > output->height ? max_height : output->height;
481} 474}
482 475
483void 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) {
484 assert(output->surface != NULL); 492 assert(output->surface != NULL);
485 493
486 struct swaybar_hotspot *hotspot, *tmp; 494 struct swaybar_hotspot *hotspot, *tmp;
@@ -506,9 +514,10 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
506 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 514 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
507 cairo_paint(cairo); 515 cairo_paint(cairo);
508 cairo_restore(cairo); 516 cairo_restore(cairo);
509 uint32_t height = render_to_cairo(cairo, bar, output); 517 uint32_t height = render_to_cairo(cairo, output);
510 if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { 518 int config_height = output->bar->config->height;
511 height = bar->config->height; 519 if (config_height >= 0 && height < (uint32_t)config_height) {
520 height = config_height;
512 } 521 }
513 if (height != output->height) { 522 if (height != output->height) {
514 // Reconfigure surface 523 // Reconfigure surface
@@ -519,11 +528,13 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
519 wl_surface_commit(output->surface); 528 wl_surface_commit(output->surface);
520 } else if (height > 0) { 529 } else if (height > 0) {
521 // Replay recording into shm and send it off 530 // Replay recording into shm and send it off
522 output->current_buffer = get_next_buffer(bar->shm, 531 output->current_buffer = get_next_buffer(output->bar->shm,
523 output->buffers, 532 output->buffers,
524 output->width * output->scale, 533 output->width * output->scale,
525 output->height * output->scale); 534 output->height * output->scale);
526 if (!output->current_buffer) { 535 if (!output->current_buffer) {
536 cairo_surface_destroy(recorder);
537 cairo_destroy(cairo);
527 return; 538 return;
528 } 539 }
529 cairo_t *shm = output->current_buffer->cairo; 540 cairo_t *shm = output->current_buffer->cairo;
@@ -541,6 +552,11 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
541 output->current_buffer->buffer, 0, 0); 552 output->current_buffer->buffer, 0, 0);
542 wl_surface_damage(output->surface, 0, 0, 553 wl_surface_damage(output->surface, 0, 0,
543 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
544 wl_surface_commit(output->surface); 560 wl_surface_commit(output->surface);
545 } 561 }
546 cairo_surface_destroy(recorder); 562 cairo_surface_destroy(recorder);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 48b43248..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,13 +87,9 @@ 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 read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read); 90 status->buffer_index = strlen(newline + 1);
71 if (read_bytes > 0) { 91 memmove(status->buffer, newline + 1, status->buffer_index + 1);
72 status->buffer_index = read_bytes; 92 return i3bar_handle_readable(status);
73 return i3bar_handle_readable(status);
74 } else {
75 return false;
76 }
77 } 93 }
78 94
79 wlr_log(WLR_DEBUG, "Using text protocol."); 95 wlr_log(WLR_DEBUG, "Using text protocol.");
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 c25c8eec..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");
@@ -845,6 +845,9 @@ static int load_config(char *path, struct swaylock_state *state,
845static struct swaylock_state state; 845static struct swaylock_state state;
846 846
847int main(int argc, char **argv) { 847int main(int argc, char **argv) {
848 wlr_log_init(WLR_DEBUG, NULL);
849 initialize_pw_backend();
850
848 enum line_mode line_mode = LM_LINE; 851 enum line_mode line_mode = LM_LINE;
849 state.args = (struct swaylock_args){ 852 state.args = (struct swaylock_args){
850 .mode = BACKGROUND_MODE_SOLID_COLOR, 853 .mode = BACKGROUND_MODE_SOLID_COLOR,
@@ -857,8 +860,6 @@ int main(int argc, char **argv) {
857 wl_list_init(&state.images); 860 wl_list_init(&state.images);
858 set_default_colors(&state.args.colors); 861 set_default_colors(&state.args.colors);
859 862
860 wlr_log_init(WLR_DEBUG, NULL);
861
862 char *config_path = NULL; 863 char *config_path = NULL;
863 int result = parse_options(argc, argv, NULL, NULL, &config_path); 864 int result = parse_options(argc, argv, NULL, NULL, &config_path);
864 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/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)