diff options
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 | */ |
98 | void container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, | 98 | bool container_resize_tiled(struct sway_container *parent, enum wlr_edges edge, |
99 | int amount); | 99 | int amount); |
100 | 100 | ||
101 | sway_cmd cmd_assign; | 101 | sway_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 | */ |
174 | struct workspace_output { | 173 | struct workspace_config { |
175 | char *output; | ||
176 | char *workspace; | 174 | char *workspace; |
175 | char *output; | ||
177 | }; | 176 | }; |
178 | 177 | ||
179 | struct bar_config { | 178 | struct 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 | ||
519 | void free_bar_config(struct bar_config *bar); | 518 | void free_bar_config(struct bar_config *bar); |
520 | 519 | ||
520 | void 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 | ||
49 | extern char *prev_workspace_name; | 49 | extern char *prev_workspace_name; |
50 | 50 | ||
51 | struct workspace_config *workspace_find_config(const char *ws_name); | ||
52 | |||
51 | struct sway_output *workspace_get_initial_output(const char *name); | 53 | struct sway_output *workspace_get_initial_output(const char *name); |
52 | 54 | ||
53 | struct sway_workspace *workspace_create(struct sway_output *output, | 55 | struct 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 | ||
98 | void bar_setup(struct swaybar *bar, | 97 | bool bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id); |
99 | const char *socket_path, | ||
100 | const char *bar_id); | ||
101 | void bar_run(struct swaybar *bar); | 98 | void bar_run(struct swaybar *bar); |
102 | void bar_teardown(struct swaybar *bar); | 99 | void 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 | |||
7 | struct 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 | |||
27 | void i3bar_block_unref(struct i3bar_block *block); | ||
28 | bool i3bar_handle_readable(struct status_line *status); | ||
29 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | ||
30 | struct i3bar_block *block, int x, int y, enum x11_button button); | ||
31 | enum x11_button wl_button_to_x11_button(uint32_t button); | ||
32 | enum 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 | ||
6 | void ipc_initialize(struct swaybar *bar, const char *bar_id); | 6 | bool ipc_initialize(struct swaybar *bar, const char *bar_id); |
7 | bool handle_ipc_readable(struct swaybar *bar); | 7 | bool handle_ipc_readable(struct swaybar *bar); |
8 | void ipc_get_workspaces(struct swaybar *bar); | 8 | void ipc_get_workspaces(struct swaybar *bar); |
9 | void ipc_send_workspace_command(struct swaybar *bar, const char *ws); | 9 | void 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 | ||
16 | struct 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 | |||
36 | struct status_line { | 16 | struct 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); | |||
55 | void status_error(struct status_line *status, const char *text); | 35 | void status_error(struct status_line *status, const char *text); |
56 | bool status_handle_readable(struct status_line *status); | 36 | bool status_handle_readable(struct status_line *status); |
57 | void status_line_free(struct status_line *status); | 37 | void status_line_free(struct status_line *status); |
58 | bool i3bar_handle_readable(struct status_line *status); | ||
59 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | ||
60 | struct i3bar_block *block, int x, int y, enum x11_button button); | ||
61 | void i3bar_block_unref(struct i3bar_block *block); | ||
62 | enum x11_button wl_button_to_x11_button(uint32_t button); | ||
63 | enum 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); | |||
101 | void render_frames(struct swaylock_state *state); | 101 | void render_frames(struct swaylock_state *state); |
102 | void damage_surface(struct swaylock_surface *surface); | 102 | void damage_surface(struct swaylock_surface *surface); |
103 | void damage_state(struct swaylock_state *state); | 103 | void damage_state(struct swaylock_state *state); |
104 | void initialize_pw_backend(void); | ||
105 | bool attempt_password(struct swaylock_password *pw); | ||
106 | void 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') | |||
22 | sysconfdir = get_option('sysconfdir') | 22 | sysconfdir = get_option('sysconfdir') |
23 | prefix = get_option('prefix') | 23 | prefix = get_option('prefix') |
24 | 24 | ||
25 | if is_freebsd | ||
26 | add_project_arguments('-D_C11_SOURCE', language: 'c') | ||
27 | endif | ||
28 | |||
25 | swayidle_deps = [] | 29 | swayidle_deps = [] |
26 | 30 | ||
27 | jsonc = dependency('json-c', version: '>=0.13') | 31 | jsonc = dependency('json-c', version: '>=0.13') |
@@ -40,7 +44,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) | |||
40 | pixman = dependency('pixman-1') | 44 | pixman = dependency('pixman-1') |
41 | libcap = dependency('libcap', required: false) | 45 | libcap = dependency('libcap', required: false) |
42 | libinput = dependency('libinput', version: '>=1.6.0') | 46 | libinput = dependency('libinput', version: '>=1.6.0') |
43 | libpam = cc.find_library('pam') | 47 | libpam = cc.find_library('pam', required: false) |
44 | systemd = dependency('libsystemd', required: false) | 48 | systemd = dependency('libsystemd', required: false) |
45 | elogind = dependency('libelogind', required: false) | 49 | elogind = dependency('libelogind', required: false) |
46 | math = cc.find_library('m') | 50 | math = cc.find_library('m') |
@@ -70,6 +74,11 @@ if elogind.found() | |||
70 | swayidle_deps += elogind | 74 | swayidle_deps += elogind |
71 | endif | 75 | endif |
72 | 76 | ||
77 | if 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') | ||
80 | endif | ||
81 | |||
73 | scdoc = find_program('scdoc', required: false) | 82 | scdoc = find_program('scdoc', required: false) |
74 | 83 | ||
75 | if scdoc.found() | 84 | if scdoc.found() |
@@ -133,9 +142,9 @@ subdir('swaymsg') | |||
133 | subdir('client') | 142 | subdir('client') |
134 | subdir('swaybg') | 143 | subdir('swaybg') |
135 | subdir('swaybar') | 144 | subdir('swaybar') |
136 | subdir('swaylock') | ||
137 | subdir('swayidle') | 145 | subdir('swayidle') |
138 | subdir('swaynag') | 146 | subdir('swaynag') |
147 | subdir('swaylock') | ||
139 | 148 | ||
140 | config = configuration_data() | 149 | config = configuration_data() |
141 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 150 | config.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 | ||
182 | static void resize_tiled(struct sway_container *parent, int amount, | 182 | static 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 | ||
322 | void container_resize_tiled(struct sway_container *parent, | 323 | bool 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 | ||
13 | static 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 | |||
27 | void free_workspace_config(struct workspace_config *wsc) { | ||
28 | free(wsc->workspace); | ||
29 | free(wsc->output); | ||
30 | free(wsc); | ||
31 | } | ||
32 | |||
13 | struct cmd_results *cmd_workspace(int argc, char **argv) { | 33 | struct 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. |
806 | int workspace_output_cmp_workspace(const void *a, const void *b) { | 811 | int 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 | ||
41 | static struct sway_transaction *transaction_create() { | 41 | static 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 | ||
87 | static void copy_output_state(struct sway_output *output, | 87 | static 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 | ||
103 | static void copy_workspace_state(struct sway_workspace *ws, | 96 | static 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 | ||
139 | static void copy_container_state(struct sway_container *container, | 126 | static 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 | ||
338 | static void transaction_progress_queue() { | 319 | static 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 | ||
91 | void ipc_init(struct sway_server *server) { | 88 | void 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 | ||
9 | Sway allows configuring swaybar in the sway configuration file. Swaybar | 9 | Sway allows configuring swaybar in the sway configuration file. |
10 | commands 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 | ||
9 | Sway allows for configuration of devices within the sway configuration file. | 9 | Sway allows for configuration of devices within the sway configuration file. |
10 | sway-input commands must be used inside an _input { }_ block in the config. | ||
11 | To obtain a list of available device identifiers, run *swaymsg -t get\_inputs*. | 10 | To 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 | ||
119 | Configure options for multiseat mode. sway-seat commands must be used inside a | 118 | Configure options for multiseat mode. |
120 | _seat { }_ block in the config. | ||
121 | 119 | ||
122 | A *seat* is a collection of input devices that act independently of each other. | 120 | A *seat* is a collection of input devices that act independently of each other. |
123 | Seats are identified by name and the default seat is _seat0_ if no seats are | 121 | Seats 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 | ||
22 | Commands 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 | ||
24 | block. For example: | ||
25 | |||
26 | ``` | ||
27 | output eDP-1 { | ||
28 | background ~/wallpaper.png | ||
29 | resolution 1920x1080 | ||
30 | } | ||
31 | ``` | ||
32 | |||
33 | is identical to | ||
34 | |||
35 | ``` | ||
36 | output eDP-1 background ~/wallpaper.png | ||
37 | output eDP-1 resolution 1920x1080 | ||
38 | ``` | ||
39 | |||
22 | These commands can be executed in your config file, via *swaymsg*(1), or via | 40 | These commands can be executed in your config file, via *swaymsg*(1), or via |
23 | the bindsym command. | 41 | the bindsym command. |
24 | 42 | ||
@@ -37,10 +55,8 @@ which you may only select one. *[...]* is used for optional arguments, and | |||
37 | 55 | ||
38 | The following commands may only be used in the configuration file. | 56 | The 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 | ||
535 | You can get a list of output names with *swaymsg -t get\_outputs*. You may also | 540 | You can get a list of output names with *swaymsg -t get\_outputs*. You may also |
536 | match any output by using the output name "\*". Be sure to add this output | 541 | match any output by using the output name "\*". |
537 | config 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 | ||
23 | struct 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 | |||
23 | struct sway_output *workspace_get_initial_output(const char *name) { | 33 | struct 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 | ||
122 | static bool workspace_valid_on_output(const char *output_name, | 128 | static 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 | ||
137 | static void workspace_name_from_binding(const struct sway_binding * binding, | 134 | static 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 | ||
481 | void bar_setup(struct swaybar *bar, | 482 | bool 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 | ||
531 | static void display_in(int fd, short mask, void *data) { | 535 | static 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 | ||
12 | void i3bar_block_unref(struct i3bar_block *block) { | 14 | void 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 | ||
144 | static void ipc_parse_config( | 144 | static 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 | ||
231 | void ipc_get_workspaces(struct swaybar *bar) { | 239 | void 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 | ||
315 | void ipc_initialize(struct swaybar *bar, const char *bar_id) { | 322 | bool 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 | ||
329 | bool handle_ipc_readable(struct swaybar *bar) { | 340 | bool 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; | |||
20 | static const double BORDER_WIDTH = 1; | 21 | static const double BORDER_WIDTH = 1; |
21 | 22 | ||
22 | static uint32_t render_status_line_error(cairo_t *cairo, | 23 | static 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 | ||
54 | static uint32_t render_status_line_text(cairo_t *cairo, | 56 | static 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 | ||
88 | static void render_sharp_line(cairo_t *cairo, uint32_t color, | 90 | static void render_sharp_line(cairo_t *cairo, uint32_t color, |
@@ -122,12 +124,11 @@ static void i3bar_block_unref_callback(void *data) { | |||
122 | 124 | ||
123 | static uint32_t render_status_block(cairo_t *cairo, | 125 | static 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 | ||
266 | static uint32_t render_status_line_i3bar(cairo_t *cairo, | 268 | static 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 | ||
282 | static uint32_t render_status_line(cairo_t *cairo, | 281 | static 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 | ||
302 | static uint32_t render_binding_mode_indicator(cairo_t *cairo, | 297 | static 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 | ||
346 | static const char *strip_workspace_number(const char *ws_name) { | 341 | static 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 | ||
368 | static uint32_t render_workspace_button(cairo_t *cairo, | 363 | static 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 | ||
440 | static uint32_t render_to_cairo(cairo_t *cairo, | 436 | static 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, | |||
845 | static struct swaylock_state state; | 845 | static struct swaylock_state state; |
846 | 846 | ||
847 | int main(int argc, char **argv) { | 847 | int 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 @@ | |||
1 | sysconfdir = get_option('sysconfdir') | 1 | sysconfdir = get_option('sysconfdir') |
2 | 2 | ||
3 | executable( | 3 | dependencies = [ |
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 | |||
15 | sources = [ | ||
16 | 'main.c', | ||
17 | 'password.c', | ||
18 | 'render.c', | ||
19 | 'seat.c' | ||
20 | ] | ||
21 | |||
22 | if libpam.found() | ||
23 | sources += ['pam.c'] | ||
24 | dependencies += [libpam] | ||
25 | else | ||
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'] | ||
29 | endif | ||
30 | |||
31 | executable('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 | |||
11 | void initialize_pw_backend(void) { | ||
12 | // TODO: only call pam_start once. keep the same handle the whole time | ||
13 | } | ||
14 | |||
15 | static 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 | |||
36 | bool 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; | ||
59 | fail: | ||
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 | ||
14 | static 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 | |||
35 | void clear_password_buffer(struct swaylock_password *pw) { | 13 | void 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 | ||
45 | static 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; | ||
69 | fail: | ||
70 | clear_password_buffer(pw); | ||
71 | return false; | ||
72 | } | ||
73 | |||
74 | static bool backspace(struct swaylock_password *pw) { | 23 | static 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 | |||
10 | static int comm[2][2]; | ||
11 | |||
12 | void 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 | |||
70 | void 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 | |||
104 | bool 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); | ||
125 | ret: | ||
126 | clear_password_buffer(pw); | ||
127 | return result; | ||
128 | } | ||