aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h2
-rw-r--r--include/sway/config.h13
-rw-r--r--include/sway/tree/workspace.h2
-rw-r--r--include/swaybar/bar.h5
-rw-r--r--include/swaybar/i3bar.h34
-rw-r--r--include/swaybar/ipc.h2
-rw-r--r--include/swaybar/status_line.h26
-rw-r--r--include/swaylock/swaylock.h3
-rw-r--r--meson.build13
-rw-r--r--sway/commands.c13
-rw-r--r--sway/commands/bar/binding_mode_indicator.c6
-rw-r--r--sway/commands/output/background.c18
-rw-r--r--sway/commands/resize.c30
-rw-r--r--sway/commands/workspace.c38
-rw-r--r--sway/config.c11
-rw-r--r--sway/config/input.c3
-rw-r--r--sway/config/output.c4
-rw-r--r--sway/desktop/transaction.c55
-rw-r--r--sway/input/cursor.c2
-rw-r--r--sway/input/input-manager.c6
-rw-r--r--sway/ipc-server.c13
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway-input.5.scd4
-rw-r--r--sway/sway.5.scd58
-rw-r--r--sway/tree/container.c13
-rw-r--r--sway/tree/view.c10
-rw-r--r--sway/tree/workspace.c49
-rw-r--r--swaybar/bar.c14
-rw-r--r--swaybar/i3bar.c2
-rw-r--r--swaybar/ipc.c19
-rw-r--r--swaybar/main.c5
-rw-r--r--swaybar/render.c110
-rw-r--r--swaybar/status_line.c42
-rw-r--r--swaylock/main.c5
-rw-r--r--swaylock/meson.build50
-rw-r--r--swaylock/pam.c62
-rw-r--r--swaylock/password.c51
-rw-r--r--swaylock/shadow.c128
38 files changed, 568 insertions, 356 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 1654eb48..370a1f7a 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;
diff --git a/include/sway/config.h b/include/sway/config.h
index 35f0e708..af5c7a18 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -167,13 +167,12 @@ struct output_config {
167}; 167};
168 168
169/** 169/**
170 * Maps a workspace name to an output name. 170 * Stores configuration for a workspace, regardless of whether the workspace
171 * 171 * exists.
172 * Set via `workspace <x> output <y>`
173 */ 172 */
174struct workspace_output { 173struct workspace_config {
175 char *output;
176 char *workspace; 174 char *workspace;
175 char *output;
177}; 176};
178 177
179struct bar_config { 178struct bar_config {
@@ -327,7 +326,7 @@ struct sway_config {
327 list_t *modes; 326 list_t *modes;
328 list_t *bars; 327 list_t *bars;
329 list_t *cmd_queue; 328 list_t *cmd_queue;
330 list_t *workspace_outputs; 329 list_t *workspace_configs;
331 list_t *output_configs; 330 list_t *output_configs;
332 list_t *input_configs; 331 list_t *input_configs;
333 list_t *seat_configs; 332 list_t *seat_configs;
@@ -518,6 +517,8 @@ struct bar_config *default_bar_config(void);
518 517
519void free_bar_config(struct bar_config *bar); 518void free_bar_config(struct bar_config *bar);
520 519
520void free_workspace_config(struct workspace_config *wsc);
521
521/** 522/**
522 * Updates the value of config->font_height based on the max title height 523 * 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 524 * reported by each container. If recalculate is true, the containers will
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index e4b616d1..c8220b39 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -48,6 +48,8 @@ struct sway_workspace {
48 48
49extern char *prev_workspace_name; 49extern char *prev_workspace_name;
50 50
51struct workspace_config *workspace_find_config(const char *ws_name);
52
51struct sway_output *workspace_get_initial_output(const char *name); 53struct sway_output *workspace_get_initial_output(const char *name);
52 54
53struct sway_workspace *workspace_create(struct sway_output *output, 55struct sway_workspace *workspace_create(struct sway_output *output,
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 29e96159..20992014 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
@@ -95,9 +94,7 @@ struct swaybar_workspace {
95 bool urgent; 94 bool urgent;
96}; 95};
97 96
98void bar_setup(struct swaybar *bar, 97bool 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); 98void bar_run(struct swaybar *bar);
102void bar_teardown(struct swaybar *bar); 99void bar_teardown(struct swaybar *bar);
103 100
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/status_line.h b/include/swaybar/status_line.h
index d3eabdf6..e6c7dac2 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -13,26 +13,6 @@ enum status_protocol {
13 PROTOCOL_I3BAR, 13 PROTOCOL_I3BAR,
14}; 14};
15 15
16struct i3bar_block {
17 struct wl_list link;
18 int ref_count;
19 char *full_text, *short_text, *align;
20 bool urgent;
21 uint32_t *color;
22 int min_width;
23 char *name, *instance;
24 bool separator;
25 int separator_block_width;
26 bool markup;
27 // Airblader features
28 uint32_t background;
29 uint32_t border;
30 int border_top;
31 int border_bottom;
32 int border_left;
33 int border_right;
34};
35
36struct status_line { 16struct status_line {
37 pid_t pid; 17 pid_t pid;
38 int read_fd, write_fd; 18 int read_fd, write_fd;
@@ -55,11 +35,5 @@ struct status_line *status_line_init(char *cmd);
55void status_error(struct status_line *status, const char *text); 35void status_error(struct status_line *status, const char *text);
56bool status_handle_readable(struct status_line *status); 36bool status_handle_readable(struct status_line *status);
57void status_line_free(struct status_line *status); 37void status_line_free(struct status_line *status);
58bool i3bar_handle_readable(struct status_line *status);
59enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
60 struct i3bar_block *block, int x, int y, enum x11_button button);
61void i3bar_block_unref(struct i3bar_block *block);
62enum x11_button wl_button_to_x11_button(uint32_t button);
63enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value);
64 38
65#endif 39#endif
diff --git a/include/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 253a4e96..de6573ea 100644
--- a/meson.build
+++ b/meson.build
@@ -22,6 +22,10 @@ datadir = get_option('datadir')
22sysconfdir = get_option('sysconfdir') 22sysconfdir = get_option('sysconfdir')
23prefix = get_option('prefix') 23prefix = get_option('prefix')
24 24
25if is_freebsd
26 add_project_arguments('-D_C11_SOURCE', language: 'c')
27endif
28
25swayidle_deps = [] 29swayidle_deps = []
26 30
27jsonc = dependency('json-c', version: '>=0.13') 31jsonc = dependency('json-c', version: '>=0.13')
@@ -40,7 +44,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
40pixman = dependency('pixman-1') 44pixman = dependency('pixman-1')
41libcap = dependency('libcap', required: false) 45libcap = dependency('libcap', required: false)
42libinput = dependency('libinput', version: '>=1.6.0') 46libinput = dependency('libinput', version: '>=1.6.0')
43libpam = cc.find_library('pam') 47libpam = cc.find_library('pam', required: false)
44systemd = dependency('libsystemd', required: false) 48systemd = dependency('libsystemd', required: false)
45elogind = dependency('libelogind', required: false) 49elogind = dependency('libelogind', required: false)
46math = cc.find_library('m') 50math = cc.find_library('m')
@@ -70,6 +74,11 @@ if elogind.found()
70 swayidle_deps += elogind 74 swayidle_deps += elogind
71endif 75endif
72 76
77if not systemd.found() and not elogind.found()
78 warning('The sway binary must be setuid when compiled without (e)logind')
79 warning('You must do this manually post-install: chmod a+s /path/to/sway')
80endif
81
73scdoc = find_program('scdoc', required: false) 82scdoc = find_program('scdoc', required: false)
74 83
75if scdoc.found() 84if scdoc.found()
@@ -133,9 +142,9 @@ subdir('swaymsg')
133subdir('client') 142subdir('client')
134subdir('swaybg') 143subdir('swaybg')
135subdir('swaybar') 144subdir('swaybar')
136subdir('swaylock')
137subdir('swayidle') 145subdir('swayidle')
138subdir('swaynag') 146subdir('swaynag')
147subdir('swaylock')
139 148
140config = configuration_data() 149config = configuration_data()
141config.set('sysconfdir', join_paths(prefix, sysconfdir)) 150config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/sway/commands.c b/sway/commands.c
index bff230f7..03761c52 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -391,14 +391,12 @@ struct cmd_results *config_command(char *exec) {
391 // Var replacement, for all but first argument of set 391 // Var replacement, for all but first argument of set
392 // TODO commands 392 // TODO commands
393 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 393 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
394 if (*argv[i] == '\"' || *argv[i] == '\'') {
395 strip_quotes(argv[i]);
396 }
394 argv[i] = do_var_replacement(argv[i]); 397 argv[i] = do_var_replacement(argv[i]);
395 unescape_string(argv[i]); 398 unescape_string(argv[i]);
396 } 399 }
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) { 400 if (handler->handle) {
403 results = handler->handle(argc-1, argv+1); 401 results = handler->handle(argc-1, argv+1);
404 } else { 402 } else {
@@ -422,11 +420,6 @@ struct cmd_results *config_subcommand(char **argv, int argc,
422 char *input = argv[0] ? argv[0] : "(empty)"; 420 char *input = argv[0] ? argv[0] : "(empty)";
423 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); 421 return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
424 } 422 }
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) { 423 if (handler->handle) {
431 return handler->handle(argc - 1, argv + 1); 424 return handler->handle(argc - 1, argv + 1);
432 } 425 }
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index 0c48bee9..f18b8d7c 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -21,7 +21,9 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
21 config->current_bar->binding_mode_indicator = false; 21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", 22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s",
23 config->current_bar->id); 23 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
24 } 27 }
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator", 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
26 "Invalid value %s", argv[0]);
27} 29}
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c
index 9e370d43..30fb47c4 100644
--- a/sway/commands/output/background.c
+++ b/sway/commands/output/background.c
@@ -123,19 +123,13 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
123 } 123 }
124 free(src); 124 free(src);
125 } else { 125 } else {
126 // Escape spaces and quotes in the final path for swaybg 126 // Escape double quotes in the final path for swaybg
127 for (size_t i = 0; i < strlen(src); i++) { 127 for (size_t i = 0; i < strlen(src); i++) {
128 switch (src[i]) { 128 if (src[i] == '"') {
129 case ' ': 129 src = realloc(src, strlen(src) + 2);
130 case '\'': 130 memmove(src + i + 1, src + i, strlen(src + i) + 1);
131 case '\"': 131 *(src + i) = '\\';
132 src = realloc(src, strlen(src) + 2); 132 i++;
133 memmove(src + i + 1, src + i, strlen(src + i) + 1);
134 *(src + i) = '\\';
135 i++;
136 break;
137 default:
138 break;
139 } 133 }
140 } 134 }
141 135
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 99e9dbda..1343b165 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -179,11 +179,11 @@ static void container_recursive_resize(struct sway_container *container,
179 } 179 }
180} 180}
181 181
182static void resize_tiled(struct sway_container *parent, int amount, 182static bool resize_tiled(struct sway_container *parent, int amount,
183 enum resize_axis axis) { 183 enum resize_axis axis) {
184 struct sway_container *focused = parent; 184 struct sway_container *focused = parent;
185 if (!parent) { 185 if (!parent) {
186 return; 186 return false;
187 } 187 }
188 188
189 enum sway_container_layout parallel_layout = 189 enum sway_container_layout parallel_layout =
@@ -216,7 +216,7 @@ static void resize_tiled(struct sway_container *parent, int amount,
216 } 216 }
217 if (!parent) { 217 if (!parent) {
218 // Can't resize in this direction 218 // Can't resize in this direction
219 return; 219 return false;
220 } 220 }
221 221
222 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
@@ -248,22 +248,22 @@ static void resize_tiled(struct sway_container *parent, int amount,
248 if (sibling_pos < parent_pos && minor_weight) { 248 if (sibling_pos < parent_pos && minor_weight) {
249 double pixels = -amount / minor_weight; 249 double pixels = -amount / minor_weight;
250 if (major_weight && (sibling_size + pixels / 2) < min_sane) { 250 if (major_weight && (sibling_size + pixels / 2) < min_sane) {
251 return; // Too small 251 return false; // Too small
252 } else if (!major_weight && sibling_size + pixels < min_sane) { 252 } else if (!major_weight && sibling_size + pixels < min_sane) {
253 return; // Too small 253 return false; // Too small
254 } 254 }
255 } else if (sibling_pos > parent_pos && major_weight) { 255 } else if (sibling_pos > parent_pos && major_weight) {
256 double pixels = -amount / major_weight; 256 double pixels = -amount / major_weight;
257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) { 257 if (minor_weight && (sibling_size + pixels / 2) < min_sane) {
258 return; // Too small 258 return false; // Too small
259 } else if (!minor_weight && sibling_size + pixels < min_sane) { 259 } else if (!minor_weight && sibling_size + pixels < min_sane) {
260 return; // Too small 260 return false; // Too small
261 } 261 }
262 } 262 }
263 } else { 263 } else {
264 double pixels = amount; 264 double pixels = amount;
265 if (parent_size + pixels < min_sane) { 265 if (parent_size + pixels < min_sane) {
266 return; // Too small 266 return false; // Too small
267 } 267 }
268 } 268 }
269 } 269 }
@@ -317,9 +317,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
317 } else { 317 } else {
318 arrange_workspace(parent->workspace); 318 arrange_workspace(parent->workspace);
319 } 319 }
320 return true;
320} 321}
321 322
322void container_resize_tiled(struct sway_container *parent, 323bool container_resize_tiled(struct sway_container *parent,
323 enum wlr_edges edge, int amount) { 324 enum wlr_edges edge, int amount) {
324 enum resize_axis axis = RESIZE_AXIS_INVALID; 325 enum resize_axis axis = RESIZE_AXIS_INVALID;
325 switch (edge) { 326 switch (edge) {
@@ -338,7 +339,7 @@ void container_resize_tiled(struct sway_container *parent,
338 case WLR_EDGE_NONE: 339 case WLR_EDGE_NONE:
339 break; 340 break;
340 } 341 }
341 resize_tiled(parent, amount, axis); 342 return resize_tiled(parent, amount, axis);
342} 343}
343 344
344/** 345/**
@@ -395,6 +396,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
395 case RESIZE_AXIS_INVALID: 396 case RESIZE_AXIS_INVALID:
396 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction"); 397 return cmd_results_new(CMD_INVALID, "resize", "Invalid axis/direction");
397 } 398 }
399 if (grow_x == 0 && grow_y == 0) {
400 return cmd_results_new(CMD_INVALID, "resize",
401 "Cannot resize any further");
402 }
398 con->x += grow_x; 403 con->x += grow_x;
399 con->y += grow_y; 404 con->y += grow_y;
400 con->width += grow_width; 405 con->width += grow_width;
@@ -442,7 +447,10 @@ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
442 } 447 }
443 } 448 }
444 449
445 resize_tiled(current, amount->amount, axis); 450 if (!resize_tiled(current, amount->amount, axis)) {
451 return cmd_results_new(CMD_INVALID, "resize",
452 "Cannot resize any further");
453 }
446 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 454 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
447} 455}
448 456
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index f026a39d..2ce7872d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -10,6 +10,26 @@
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 list_add(config->workspace_configs, wsc);
24 return wsc;
25}
26
27void free_workspace_config(struct workspace_config *wsc) {
28 free(wsc->workspace);
29 free(wsc->output);
30 free(wsc);
31}
32
13struct cmd_results *cmd_workspace(int argc, char **argv) { 33struct cmd_results *cmd_workspace(int argc, char **argv) {
14 struct cmd_results *error = NULL; 34 struct cmd_results *error = NULL;
15 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { 35 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
@@ -28,21 +48,15 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
28 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { 48 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) {
29 return error; 49 return error;
30 } 50 }
31 struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); 51 char *ws_name = join_args(argv, argc - 2);
32 if (!wso) { 52 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
53 free(ws_name);
54 if (!wsc) {
33 return cmd_results_new(CMD_FAILURE, "workspace output", 55 return cmd_results_new(CMD_FAILURE, "workspace output",
34 "Unable to allocate workspace output"); 56 "Unable to allocate workspace output");
35 } 57 }
36 wso->workspace = join_args(argv, argc - 2); 58 free(wsc->output);
37 wso->output = strdup(argv[output_location + 1]); 59 wsc->output = strdup(argv[output_location + 1]);
38 int i = -1;
39 if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) {
40 struct workspace_output *old = config->workspace_outputs->items[i];
41 free(old); // workspaces can only be assigned to a single output
42 list_del(config->workspace_outputs, i);
43 }
44 wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output);
45 list_add(config->workspace_outputs, wso);
46 } else { 60 } else {
47 if (config->reading || !config->active) { 61 if (config->reading || !config->active) {
48 return cmd_results_new(CMD_DEFER, "workspace", NULL); 62 return cmd_results_new(CMD_DEFER, "workspace", NULL);
diff --git a/sway/config.c b/sway/config.c
index 830fb65f..1e08559d 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -95,7 +95,12 @@ void free_config(struct sway_config *config) {
95 list_free(config->bars); 95 list_free(config->bars);
96 } 96 }
97 list_free(config->cmd_queue); 97 list_free(config->cmd_queue);
98 list_free(config->workspace_outputs); 98 if (config->workspace_configs) {
99 for (int i = 0; i < config->workspace_configs->length; i++) {
100 free_workspace_config(config->workspace_configs->items[i]);
101 }
102 list_free(config->workspace_configs);
103 }
99 if (config->output_configs) { 104 if (config->output_configs) {
100 for (int i = 0; i < config->output_configs->length; i++) { 105 for (int i = 0; i < config->output_configs->length; i++) {
101 free_output_config(config->output_configs->items[i]); 106 free_output_config(config->output_configs->items[i]);
@@ -175,7 +180,7 @@ static void config_defaults(struct sway_config *config) {
175 if (!(config->symbols = create_list())) goto cleanup; 180 if (!(config->symbols = create_list())) goto cleanup;
176 if (!(config->modes = create_list())) goto cleanup; 181 if (!(config->modes = create_list())) goto cleanup;
177 if (!(config->bars = create_list())) goto cleanup; 182 if (!(config->bars = create_list())) goto cleanup;
178 if (!(config->workspace_outputs = create_list())) goto cleanup; 183 if (!(config->workspace_configs = create_list())) goto cleanup;
179 if (!(config->criteria = create_list())) goto cleanup; 184 if (!(config->criteria = create_list())) goto cleanup;
180 if (!(config->no_focus = create_list())) goto cleanup; 185 if (!(config->no_focus = create_list())) goto cleanup;
181 if (!(config->input_configs = create_list())) goto cleanup; 186 if (!(config->input_configs = create_list())) goto cleanup;
@@ -804,7 +809,7 @@ char *do_var_replacement(char *str) {
804// would compare two structs in full, while this method only compares the 809// would compare two structs in full, while this method only compares the
805// workspace. 810// workspace.
806int workspace_output_cmp_workspace(const void *a, const void *b) { 811int workspace_output_cmp_workspace(const void *a, const void *b) {
807 const struct workspace_output *wsa = a, *wsb = b; 812 const struct workspace_config *wsa = a, *wsb = b;
808 return lenient_strcmp(wsa->workspace, wsb->workspace); 813 return lenient_strcmp(wsa->workspace, wsb->workspace);
809} 814}
810 815
diff --git a/sway/config/input.c b/sway/config/input.c
index fffc8518..6b43a5b9 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -52,6 +52,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
52 if (src->dwt != INT_MIN) { 52 if (src->dwt != INT_MIN) {
53 dst->dwt = src->dwt; 53 dst->dwt = src->dwt;
54 } 54 }
55 if (src->left_handed != INT_MIN) {
56 dst->left_handed = src->left_handed;
57 }
55 if (src->middle_emulation != INT_MIN) { 58 if (src->middle_emulation != INT_MIN) {
56 dst->middle_emulation = src->middle_emulation; 59 dst->middle_emulation = src->middle_emulation;
57 } 60 }
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/desktop/transaction.c b/sway/desktop/transaction.c
index 797f6b4c..34d99d52 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;
@@ -301,15 +282,15 @@ static void transaction_apply(struct sway_transaction *transaction) {
301 case N_ROOT: 282 case N_ROOT:
302 break; 283 break;
303 case N_OUTPUT: 284 case N_OUTPUT:
304 apply_output_state(node->sway_output, instruction->output_state); 285 apply_output_state(node->sway_output, &instruction->output_state);
305 break; 286 break;
306 case N_WORKSPACE: 287 case N_WORKSPACE:
307 apply_workspace_state(node->sway_workspace, 288 apply_workspace_state(node->sway_workspace,
308 instruction->workspace_state); 289 &instruction->workspace_state);
309 break; 290 break;
310 case N_CONTAINER: 291 case N_CONTAINER:
311 apply_container_state(node->sway_container, 292 apply_container_state(node->sway_container,
312 instruction->container_state); 293 &instruction->container_state);
313 break; 294 break;
314 } 295 }
315 296
@@ -335,7 +316,7 @@ static bool transaction_same_nodes(struct sway_transaction *a,
335 return true; 316 return true;
336} 317}
337 318
338static void transaction_progress_queue() { 319static void transaction_progress_queue(void) {
339 if (!server.transactions->length) { 320 if (!server.transactions->length) {
340 return; 321 return;
341 } 322 }
@@ -390,7 +371,7 @@ static bool should_configure(struct sway_node *node,
390 return false; 371 return false;
391 } 372 }
392 struct sway_container_state *cstate = &node->sway_container->current; 373 struct sway_container_state *cstate = &node->sway_container->current;
393 struct sway_container_state *istate = instruction->container_state; 374 struct sway_container_state *istate = &instruction->container_state;
394#ifdef HAVE_XWAYLAND 375#ifdef HAVE_XWAYLAND
395 // Xwayland views are position-aware and need to be reconfigured 376 // Xwayland views are position-aware and need to be reconfigured
396 // when their position changes. 377 // when their position changes.
@@ -418,10 +399,10 @@ static void transaction_commit(struct sway_transaction *transaction) {
418 struct sway_node *node = instruction->node; 399 struct sway_node *node = instruction->node;
419 if (should_configure(node, instruction)) { 400 if (should_configure(node, instruction)) {
420 instruction->serial = view_configure(node->sway_container->view, 401 instruction->serial = view_configure(node->sway_container->view,
421 instruction->container_state->view_x, 402 instruction->container_state.view_x,
422 instruction->container_state->view_y, 403 instruction->container_state.view_y,
423 instruction->container_state->view_width, 404 instruction->container_state.view_width,
424 instruction->container_state->view_height); 405 instruction->container_state.view_height);
425 ++transaction->num_waiting; 406 ++transaction->num_waiting;
426 407
427 // From here on we are rendering a saved buffer of the view, which 408 // From here on we are rendering a saved buffer of the view, which
@@ -513,8 +494,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
513 int width, int height) { 494 int width, int height) {
514 struct sway_transaction_instruction *instruction = 495 struct sway_transaction_instruction *instruction =
515 view->container->node.instruction; 496 view->container->node.instruction;
516 if (instruction->container_state->view_width == width && 497 if (instruction->container_state.view_width == width &&
517 instruction->container_state->view_height == height) { 498 instruction->container_state.view_height == height) {
518 set_instruction_ready(instruction); 499 set_instruction_ready(instruction);
519 } 500 }
520} 501}
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 8c9f65ab..2d5d351f 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -897,7 +897,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
897 897
898 // Handle moving a tiling container 898 // Handle moving a tiling container
899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED && 899 if (config->tiling_drag && mod_pressed && state == WLR_BUTTON_PRESSED &&
900 !is_floating_or_child && !cont->is_fullscreen) { 900 !is_floating_or_child && cont && !cont->is_fullscreen) {
901 seat_pointer_notify_button(seat, time_msec, button, state); 901 seat_pointer_notify_button(seat, time_msec, button, state);
902 seat_begin_move_tiling(seat, cont, button); 902 seat_begin_move_tiling(seat, cont, button);
903 return; 903 return;
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index f696646f..f39fe29c 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -233,7 +233,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
233 wlr_log(WLR_DEBUG, "adding device: '%s'", 233 wlr_log(WLR_DEBUG, "adding device: '%s'",
234 input_device->identifier); 234 input_device->identifier);
235 235
236 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 236 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
237 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
237 input_manager_libinput_config_pointer(input_device); 238 input_manager_libinput_config_pointer(input_device);
238 } 239 }
239 240
@@ -393,7 +394,8 @@ void input_manager_apply_input_config(struct sway_input_manager *input,
393 wl_list_for_each(input_device, &input->devices, link) { 394 wl_list_for_each(input_device, &input->devices, link) {
394 if (strcmp(input_device->identifier, input_config->identifier) == 0 395 if (strcmp(input_device->identifier, input_config->identifier) == 0
395 || wildcard) { 396 || wildcard) {
396 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { 397 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
398 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
397 input_manager_libinput_config_pointer(input_device); 399 input_manager_libinput_config_pointer(input_device);
398 } 400 }
399 401
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 99959c97..2d915502 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -1,8 +1,5 @@
1// See https://i3wm.org/docs/ipc.html for protocol information 1// See https://i3wm.org/docs/ipc.html for protocol information
2#ifndef __FreeBSD__ 2#define _POSIX_C_SOURCE 200112L
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700
5#endif
6#ifdef __linux__ 3#ifdef __linux__
7#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
8#elif __FreeBSD__ 5#elif __FreeBSD__
@@ -89,10 +86,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
89} 86}
90 87
91void ipc_init(struct sway_server *server) { 88void ipc_init(struct sway_server *server) {
92 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); 89 ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0);
93 if (ipc_socket == -1) { 90 if (ipc_socket == -1) {
94 sway_abort("Unable to create IPC socket"); 91 sway_abort("Unable to create IPC socket");
95 } 92 }
93 if (fcntl(ipc_socket, F_SETFD, FD_CLOEXEC) == -1) {
94 sway_abort("Unable to set CLOEXEC on IPC socket");
95 }
96 if (fcntl(ipc_socket, F_SETFL, O_NONBLOCK) == -1) {
97 sway_abort("Unable to set NONBLOCK on IPC socket");
98 }
96 99
97 ipc_sockaddr = ipc_user_sockaddr(); 100 ipc_sockaddr = ipc_user_sockaddr();
98 101
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..5736d70a 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
@@ -116,8 +115,7 @@ The following commands may only be used in the configuration file.
116 115
117## SEAT CONFIGURATION 116## SEAT CONFIGURATION
118 117
119Configure options for multiseat mode. sway-seat commands must be used inside a 118Configure options for multiseat mode.
120_seat { }_ block in the config.
121 119
122A *seat* is a collection of input devices that act independently of each other. 120A *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 121Seats 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..1526eee3 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.
@@ -424,20 +436,15 @@ The default colors are:
424*hide\_edge\_borders* none|vertical|horizontal|both|smart 436*hide\_edge\_borders* none|vertical|horizontal|both|smart
425 Hides window borders adjacent to the screen edges. Default is _none_. 437 Hides window borders adjacent to the screen edges. Default is _none_.
426 438
427*input* <input\_device> *{* <commands...> *}* 439*input* <input\_device> <input-subcommands...>
428 _commands..._ after *{* will be interpreted as input commands applying to 440 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 441
433 \* may be used in lieu of a specific device name to configure all input 442 \* 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 443 devices. A list of input device names may be obtained via *swaymsg -t
435 get\_inputs*. 444 get\_inputs*.
436 445
437*seat* <seat> *{* <commands...> *}* 446*seat* <seat> <seat-subcommands...>
438 _commands..._ after *{* will be interpreted as seat commands applying to 447 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 448
442*seat* <seat> cursor move|set <x> <y> 449*seat* <seat> cursor move|set <x> <y>
443 Move specified seat's cursor relative to current position or wrap to 450 Move specified seat's cursor relative to current position or wrap to
@@ -465,10 +472,8 @@ The default colors are:
465*mode* <mode> 472*mode* <mode>
466 Switches to the specified mode. The default mode _default_. 473 Switches to the specified mode. The default mode _default_.
467 474
468*mode* [--pango\_markup] <mode> *{* <commands...> *}* 475*mode* [--pango\_markup] <mode> <mode-subcommands...>
469 _commands..._ after *{* will be added to the specified mode. A newline is 476 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 477 If _--pango\_markup_ is given, then _mode_ will be interpreted as pango
473 markup. 478 markup.
474 479
@@ -533,8 +538,15 @@ You may combine output commands into one, like so:
533 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch 538 output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch
534 539
535You can get a list of output names with *swaymsg -t get\_outputs*. You may also 540You 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 541match any output by using the output name "\*".
537config after the others, or it will be matched instead of the others. 542
543*set* $<name> <value>
544 Sets variable $_name_ to _value_. You can use the new variable in the
545 arguments of future commands. When the variable is used, it can be escaped
546 with an additional $ (ie $$_name_) to have the replacement happen at run
547 time instead of when reading the config. However, it does not always make
548 sense for the variable to be replaced at run time since some arguments do
549 need to be known at config time.
538 550
539*show\_marks* yes|no 551*show\_marks* yes|no
540 If *show\_marks* is yes, marks will be displayed in the window borders. 552 If *show\_marks* is yes, marks will be displayed in the window borders.
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 53b127b7..baaa82fd 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -67,12 +67,10 @@ void container_destroy(struct sway_container *con) {
67 list_free(con->outputs); 67 list_free(con->outputs);
68 68
69 if (con->view) { 69 if (con->view) {
70 struct sway_view *view = con->view; 70 if (con->view->container == con) {
71 view->container = NULL; 71 con->view->container = NULL;
72 free(view->title_format); 72 }
73 view->title_format = NULL; 73 if (con->view->destroying) {
74
75 if (view->destroying) {
76 view_destroy(con->view); 74 view_destroy(con->view);
77 } 75 }
78 } 76 }
@@ -985,7 +983,8 @@ void container_discover_outputs(struct sway_container *con) {
985 } 983 }
986 } 984 }
987 struct sway_output *new_output = container_get_effective_output(con); 985 struct sway_output *new_output = container_get_effective_output(con);
988 double old_scale = old_output ? old_output->wlr_output->scale : -1; 986 double old_scale = old_output && old_output->enabled ?
987 old_output->wlr_output->scale : -1;
989 double new_scale = new_output ? new_output->wlr_output->scale : -1; 988 double new_scale = new_output ? new_output->wlr_output->scale : -1;
990 if (old_scale != new_scale) { 989 if (old_scale != new_scale) {
991 container_update_title_textures(con); 990 container_update_title_textures(con);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index e370443c..a024f325 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -989,12 +989,16 @@ bool view_is_visible(struct sway_view *view) {
989 floater = floater->parent; 989 floater = floater->parent;
990 } 990 }
991 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 991 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
992 if (!is_sticky && !workspace_is_visible(workspace)) {
993 return false;
994 }
992 // Check view isn't in a tabbed or stacked container on an inactive tab 995 // Check view isn't in a tabbed or stacked container on an inactive tab
993 struct sway_seat *seat = input_manager_current_seat(input_manager); 996 struct sway_seat *seat = input_manager_current_seat(input_manager);
994 struct sway_container *con = view->container; 997 struct sway_container *con = view->container;
995 while (con) { 998 while (con) {
996 enum sway_container_layout layout = container_parent_layout(con); 999 enum sway_container_layout layout = container_parent_layout(con);
997 if (layout == L_TABBED || layout == L_STACKED) { 1000 if ((layout == L_TABBED || layout == L_STACKED)
1001 && !container_is_floating(con)) {
998 struct sway_node *parent = con->parent ? 1002 struct sway_node *parent = con->parent ?
999 &con->parent->node : &con->workspace->node; 1003 &con->parent->node : &con->workspace->node;
1000 if (seat_get_active_tiling_child(seat, parent) != &con->node) { 1004 if (seat_get_active_tiling_child(seat, parent) != &con->node) {
@@ -1008,10 +1012,6 @@ bool view_is_visible(struct sway_view *view) {
1008 !container_is_fullscreen_or_child(view->container)) { 1012 !container_is_fullscreen_or_child(view->container)) {
1009 return false; 1013 return false;
1010 } 1014 }
1011 // Check the workspace is visible
1012 if (!is_sticky) {
1013 return workspace_is_visible(workspace);
1014 }
1015 return true; 1015 return true;
1016} 1016}
1017 1017
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 16031e87..d65a3e4c 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
@@ -121,17 +127,8 @@ void next_name_map(struct sway_container *ws, void *data) {
121 127
122static bool workspace_valid_on_output(const char *output_name, 128static bool workspace_valid_on_output(const char *output_name,
123 const char *ws_name) { 129 const char *ws_name) {
124 int i; 130 struct workspace_config *wsc = workspace_find_config(ws_name);
125 for (i = 0; i < config->workspace_outputs->length; ++i) { 131 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} 132}
136 133
137static void workspace_name_from_binding(const struct sway_binding * binding, 134static void workspace_name_from_binding(const struct sway_binding * binding,
@@ -231,13 +228,13 @@ char *workspace_next_name(const char *output_name) {
231 workspace_name_from_binding(mode->keycode_bindings->items[i], 228 workspace_name_from_binding(mode->keycode_bindings->items[i],
232 output_name, &order, &target); 229 output_name, &order, &target);
233 } 230 }
234 for (int i = 0; i < config->workspace_outputs->length; ++i) { 231 for (int i = 0; i < config->workspace_configs->length; ++i) {
235 // Unlike with bindings, this does not guarantee order 232 // Unlike with bindings, this does not guarantee order
236 const struct workspace_output *wso = config->workspace_outputs->items[i]; 233 const struct workspace_config *wsc = config->workspace_configs->items[i];
237 if (strcmp(wso->output, output_name) == 0 234 if (wsc->output && strcmp(wsc->output, output_name) == 0
238 && workspace_by_name(wso->workspace) == NULL) { 235 && workspace_by_name(wsc->workspace) == NULL) {
239 free(target); 236 free(target);
240 target = strdup(wso->workspace); 237 target = strdup(wsc->workspace);
241 break; 238 break;
242 } 239 }
243 } 240 }
diff --git a/swaybar/bar.c b/swaybar/bar.c
index ab307fd4..15e81976 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"
@@ -478,14 +479,16 @@ static void render_all_frames(struct swaybar *bar) {
478 } 479 }
479} 480}
480 481
481void bar_setup(struct swaybar *bar, 482bool bar_setup(struct swaybar *bar,
482 const char *socket_path, const char *bar_id) { 483 const char *socket_path, const char *bar_id) {
483 bar_init(bar); 484 bar_init(bar);
484 init_event_loop(); 485 init_event_loop();
485 486
486 bar->ipc_socketfd = ipc_open_socket(socket_path); 487 bar->ipc_socketfd = ipc_open_socket(socket_path);
487 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 488 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
488 ipc_initialize(bar, bar_id); 489 if (!ipc_initialize(bar, bar_id)) {
490 return false;
491 }
489 if (bar->config->status_command) { 492 if (bar->config->status_command) {
490 bar->status = status_line_init(bar->config->status_command); 493 bar->status = status_line_init(bar->config->status_command);
491 } 494 }
@@ -526,6 +529,7 @@ void bar_setup(struct swaybar *bar,
526 529
527 ipc_get_workspaces(bar); 530 ipc_get_workspaces(bar);
528 render_all_frames(bar); 531 render_all_frames(bar);
532 return true;
529} 533}
530 534
531static void display_in(int fd, short mask, void *data) { 535static void display_in(int fd, short mask, void *data) {
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 325aa61a..8e9b038b 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -6,7 +6,9 @@
6#include <string.h> 6#include <string.h>
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/util/log.h> 8#include <wlr/util/log.h>
9#include "swaybar/bar.h"
9#include "swaybar/config.h" 10#include "swaybar/config.h"
11#include "swaybar/i3bar.h"
10#include "swaybar/status_line.h" 12#include "swaybar/status_line.h"
11 13
12void i3bar_block_unref(struct i3bar_block *block) { 14void i3bar_block_unref(struct i3bar_block *block) {
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/render.c b/swaybar/render.c
index 9413dc57..90e5bac7 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,22 +454,19 @@ 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
@@ -506,9 +499,10 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
506 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 499 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
507 cairo_paint(cairo); 500 cairo_paint(cairo);
508 cairo_restore(cairo); 501 cairo_restore(cairo);
509 uint32_t height = render_to_cairo(cairo, bar, output); 502 uint32_t height = render_to_cairo(cairo, output);
510 if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { 503 int config_height = output->bar->config->height;
511 height = bar->config->height; 504 if (config_height >= 0 && height < (uint32_t)config_height) {
505 height = config_height;
512 } 506 }
513 if (height != output->height) { 507 if (height != output->height) {
514 // Reconfigure surface 508 // Reconfigure surface
@@ -519,7 +513,7 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
519 wl_surface_commit(output->surface); 513 wl_surface_commit(output->surface);
520 } else if (height > 0) { 514 } else if (height > 0) {
521 // Replay recording into shm and send it off 515 // Replay recording into shm and send it off
522 output->current_buffer = get_next_buffer(bar->shm, 516 output->current_buffer = get_next_buffer(output->bar->shm,
523 output->buffers, 517 output->buffers,
524 output->width * output->scale, 518 output->width * output->scale,
525 output->height * output->scale); 519 output->height * output->scale);
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/swaylock/main.c b/swaylock/main.c
index c25c8eec..693cbc10 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -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..6c87d173 100644
--- a/swaylock/meson.build
+++ b/swaylock/meson.build
@@ -1,25 +1,37 @@
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],
24 install: true 36 install: true
25) 37)
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}