diff options
70 files changed, 1131 insertions, 764 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h index 1654eb48..64f707f4 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -95,7 +95,7 @@ struct cmd_results *add_color(const char *name, | |||
95 | /** | 95 | /** |
96 | * TODO: Move this function and its dependent functions to container.c. | 96 | * TODO: Move this function and its dependent functions to container.c. |
97 | */ | 97 | */ |
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; |
@@ -160,6 +160,7 @@ sway_cmd cmd_scratchpad; | |||
160 | sway_cmd cmd_seamless_mouse; | 160 | sway_cmd cmd_seamless_mouse; |
161 | sway_cmd cmd_set; | 161 | sway_cmd cmd_set; |
162 | sway_cmd cmd_show_marks; | 162 | sway_cmd cmd_show_marks; |
163 | sway_cmd cmd_smart_borders; | ||
163 | sway_cmd cmd_smart_gaps; | 164 | sway_cmd cmd_smart_gaps; |
164 | sway_cmd cmd_split; | 165 | sway_cmd cmd_split; |
165 | sway_cmd cmd_splith; | 166 | sway_cmd cmd_splith; |
@@ -217,6 +218,7 @@ sway_cmd bar_colors_cmd_urgent_workspace; | |||
217 | sway_cmd input_cmd_seat; | 218 | sway_cmd input_cmd_seat; |
218 | sway_cmd input_cmd_accel_profile; | 219 | sway_cmd input_cmd_accel_profile; |
219 | sway_cmd input_cmd_click_method; | 220 | sway_cmd input_cmd_click_method; |
221 | sway_cmd input_cmd_drag; | ||
220 | sway_cmd input_cmd_drag_lock; | 222 | sway_cmd input_cmd_drag_lock; |
221 | sway_cmd input_cmd_dwt; | 223 | sway_cmd input_cmd_dwt; |
222 | sway_cmd input_cmd_events; | 224 | sway_cmd input_cmd_events; |
diff --git a/include/sway/config.h b/include/sway/config.h index 35f0e708..98a18b76 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -93,6 +93,7 @@ struct input_config { | |||
93 | 93 | ||
94 | int accel_profile; | 94 | int accel_profile; |
95 | int click_method; | 95 | int click_method; |
96 | int drag; | ||
96 | int drag_lock; | 97 | int drag_lock; |
97 | int dwt; | 98 | int dwt; |
98 | int left_handed; | 99 | int left_handed; |
@@ -167,13 +168,14 @@ struct output_config { | |||
167 | }; | 168 | }; |
168 | 169 | ||
169 | /** | 170 | /** |
170 | * Maps a workspace name to an output name. | 171 | * Stores configuration for a workspace, regardless of whether the workspace |
171 | * | 172 | * exists. |
172 | * Set via `workspace <x> output <y>` | ||
173 | */ | 173 | */ |
174 | struct workspace_output { | 174 | struct workspace_config { |
175 | char *output; | ||
176 | char *workspace; | 175 | char *workspace; |
176 | char *output; | ||
177 | int gaps_inner; | ||
178 | int gaps_outer; | ||
177 | }; | 179 | }; |
178 | 180 | ||
179 | struct bar_config { | 181 | struct bar_config { |
@@ -251,7 +253,8 @@ enum edge_border_types { | |||
251 | E_VERTICAL, /**< hide vertical edge borders */ | 253 | E_VERTICAL, /**< hide vertical edge borders */ |
252 | E_HORIZONTAL, /**< hide horizontal edge borders */ | 254 | E_HORIZONTAL, /**< hide horizontal edge borders */ |
253 | E_BOTH, /**< hide vertical and horizontal edge borders */ | 255 | E_BOTH, /**< hide vertical and horizontal edge borders */ |
254 | E_SMART /**< hide both if precisely one window is present in workspace */ | 256 | E_SMART, /**< hide both if precisely one window is present in workspace */ |
257 | E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */ | ||
255 | }; | 258 | }; |
256 | 259 | ||
257 | enum command_context { | 260 | enum command_context { |
@@ -327,7 +330,7 @@ struct sway_config { | |||
327 | list_t *modes; | 330 | list_t *modes; |
328 | list_t *bars; | 331 | list_t *bars; |
329 | list_t *cmd_queue; | 332 | list_t *cmd_queue; |
330 | list_t *workspace_outputs; | 333 | list_t *workspace_configs; |
331 | list_t *output_configs; | 334 | list_t *output_configs; |
332 | list_t *input_configs; | 335 | list_t *input_configs; |
333 | list_t *seat_configs; | 336 | list_t *seat_configs; |
@@ -381,6 +384,7 @@ struct sway_config { | |||
381 | int border_thickness; | 384 | int border_thickness; |
382 | int floating_border_thickness; | 385 | int floating_border_thickness; |
383 | enum edge_border_types hide_edge_borders; | 386 | enum edge_border_types hide_edge_borders; |
387 | enum edge_border_types saved_edge_borders; | ||
384 | 388 | ||
385 | // border colors | 389 | // border colors |
386 | struct { | 390 | struct { |
@@ -450,8 +454,6 @@ void free_sway_variable(struct sway_variable *var); | |||
450 | */ | 454 | */ |
451 | char *do_var_replacement(char *str); | 455 | char *do_var_replacement(char *str); |
452 | 456 | ||
453 | struct cmd_results *check_security_config(); | ||
454 | |||
455 | int input_identifier_cmp(const void *item, const void *data); | 457 | int input_identifier_cmp(const void *item, const void *data); |
456 | 458 | ||
457 | struct input_config *new_input_config(const char* identifier); | 459 | struct input_config *new_input_config(const char* identifier); |
@@ -472,7 +474,7 @@ struct seat_config *copy_seat_config(struct seat_config *seat); | |||
472 | 474 | ||
473 | void free_seat_config(struct seat_config *ic); | 475 | void free_seat_config(struct seat_config *ic); |
474 | 476 | ||
475 | struct seat_attachment_config *seat_attachment_config_new(); | 477 | struct seat_attachment_config *seat_attachment_config_new(void); |
476 | 478 | ||
477 | struct seat_attachment_config *seat_config_get_attachment( | 479 | struct seat_attachment_config *seat_config_get_attachment( |
478 | struct seat_config *seat_config, char *identifier); | 480 | struct seat_config *seat_config, char *identifier); |
@@ -518,6 +520,8 @@ struct bar_config *default_bar_config(void); | |||
518 | 520 | ||
519 | void free_bar_config(struct bar_config *bar); | 521 | void free_bar_config(struct bar_config *bar); |
520 | 522 | ||
523 | void free_workspace_config(struct workspace_config *wsc); | ||
524 | |||
521 | /** | 525 | /** |
522 | * Updates the value of config->font_height based on the max title height | 526 | * Updates the value of config->font_height based on the max title height |
523 | * reported by each container. If recalculate is true, the containers will | 527 | * reported by each container. If recalculate is true, the containers will |
diff --git a/include/sway/debug.h b/include/sway/debug.h index bf3a5f6d..0e9bb056 100644 --- a/include/sway/debug.h +++ b/include/sway/debug.h | |||
@@ -17,6 +17,6 @@ struct sway_debug { | |||
17 | 17 | ||
18 | extern struct sway_debug debug; | 18 | extern struct sway_debug debug; |
19 | 19 | ||
20 | void update_debug_tree(); | 20 | void update_debug_tree(void); |
21 | 21 | ||
22 | #endif | 22 | #endif |
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index fef243e3..1cbfd15d 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include "sway/tree/container.h" | 4 | #include "sway/tree/container.h" |
5 | #include "sway/input/input-manager.h" | 5 | #include "sway/input/input-manager.h" |
6 | 6 | ||
7 | json_object *ipc_json_get_version(); | 7 | json_object *ipc_json_get_version(void); |
8 | 8 | ||
9 | json_object *ipc_json_describe_disabled_output(struct sway_output *o); | 9 | json_object *ipc_json_describe_disabled_output(struct sway_output *o); |
10 | json_object *ipc_json_describe_node(struct sway_node *node); | 10 | json_object *ipc_json_describe_node(struct sway_node *node); |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 6019602c..da6592b4 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -94,9 +94,6 @@ struct sway_container { | |||
94 | 94 | ||
95 | // The gaps currently applied to the container. | 95 | // The gaps currently applied to the container. |
96 | double current_gaps; | 96 | double current_gaps; |
97 | bool has_gaps; | ||
98 | double gaps_inner; | ||
99 | double gaps_outer; | ||
100 | 97 | ||
101 | struct sway_workspace *workspace; // NULL when hidden in the scratchpad | 98 | struct sway_workspace *workspace; // NULL when hidden in the scratchpad |
102 | struct sway_container *parent; // NULL if container in root of workspace | 99 | struct sway_container *parent; // NULL if container in root of workspace |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 2c7b4c2b..028be536 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -265,6 +265,13 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
265 | int height); | 265 | int height); |
266 | 266 | ||
267 | /** | 267 | /** |
268 | * Whether or not the view is the only visible view in its tree. If the view | ||
269 | * is tiling, there may be floating views. If the view is floating, there may | ||
270 | * be tiling views or views in a different floating container. | ||
271 | */ | ||
272 | bool view_is_only_visible(struct sway_view *view); | ||
273 | |||
274 | /** | ||
268 | * Configure the view's position and size based on the container's position and | 275 | * Configure the view's position and size based on the container's position and |
269 | * size, taking borders into consideration. | 276 | * size, taking borders into consideration. |
270 | */ | 277 | */ |
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index e4b616d1..efdae5a1 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h | |||
@@ -32,10 +32,9 @@ struct sway_workspace { | |||
32 | enum sway_container_layout layout; | 32 | enum sway_container_layout layout; |
33 | enum sway_container_layout prev_split_layout; | 33 | enum sway_container_layout prev_split_layout; |
34 | 34 | ||
35 | double current_gaps; | 35 | int current_gaps; |
36 | bool has_gaps; | 36 | int gaps_inner; |
37 | double gaps_inner; | 37 | int gaps_outer; |
38 | double gaps_outer; | ||
39 | 38 | ||
40 | struct sway_output *output; // NULL if no outputs are connected | 39 | struct sway_output *output; // NULL if no outputs are connected |
41 | list_t *floating; // struct sway_container | 40 | list_t *floating; // struct sway_container |
@@ -48,6 +47,8 @@ struct sway_workspace { | |||
48 | 47 | ||
49 | extern char *prev_workspace_name; | 48 | extern char *prev_workspace_name; |
50 | 49 | ||
50 | struct workspace_config *workspace_find_config(const char *ws_name); | ||
51 | |||
51 | struct sway_output *workspace_get_initial_output(const char *name); | 52 | struct sway_output *workspace_get_initial_output(const char *name); |
52 | 53 | ||
53 | struct sway_workspace *workspace_create(struct sway_output *output, | 54 | struct sway_workspace *workspace_create(struct sway_output *output, |
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 29e96159..de234111 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h | |||
@@ -54,7 +54,6 @@ struct swaybar { | |||
54 | struct wl_seat *seat; | 54 | struct wl_seat *seat; |
55 | 55 | ||
56 | struct swaybar_config *config; | 56 | struct swaybar_config *config; |
57 | struct swaybar_output *focused_output; | ||
58 | struct swaybar_pointer pointer; | 57 | struct swaybar_pointer pointer; |
59 | struct status_line *status; | 58 | struct status_line *status; |
60 | 59 | ||
@@ -84,6 +83,8 @@ struct swaybar_output { | |||
84 | enum wl_output_subpixel subpixel; | 83 | enum wl_output_subpixel subpixel; |
85 | struct pool_buffer buffers[2]; | 84 | struct pool_buffer buffers[2]; |
86 | struct pool_buffer *current_buffer; | 85 | struct pool_buffer *current_buffer; |
86 | bool dirty; | ||
87 | bool frame_scheduled; | ||
87 | }; | 88 | }; |
88 | 89 | ||
89 | struct swaybar_workspace { | 90 | struct swaybar_workspace { |
@@ -95,9 +96,7 @@ struct swaybar_workspace { | |||
95 | bool urgent; | 96 | bool urgent; |
96 | }; | 97 | }; |
97 | 98 | ||
98 | void bar_setup(struct swaybar *bar, | 99 | 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); | 100 | void bar_run(struct swaybar *bar); |
102 | void bar_teardown(struct swaybar *bar); | 101 | void bar_teardown(struct swaybar *bar); |
103 | 102 | ||
diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 6739c28a..5f5688cf 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h | |||
@@ -50,7 +50,7 @@ struct swaybar_config { | |||
50 | } colors; | 50 | } colors; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | struct swaybar_config *init_config(); | 53 | struct swaybar_config *init_config(void); |
54 | void free_config(struct swaybar_config *config); | 54 | void free_config(struct swaybar_config *config); |
55 | uint32_t parse_position(const char *position); | 55 | uint32_t parse_position(const char *position); |
56 | 56 | ||
diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h new file mode 100644 index 00000000..12d9b317 --- /dev/null +++ b/include/swaybar/i3bar.h | |||
@@ -0,0 +1,34 @@ | |||
1 | #ifndef _SWAYBAR_I3BAR_H | ||
2 | #define _SWAYBAR_I3BAR_H | ||
3 | |||
4 | #include "bar.h" | ||
5 | #include "status_line.h" | ||
6 | |||
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/render.h b/include/swaybar/render.h index 071e2298..ebdc69e4 100644 --- a/include/swaybar/render.h +++ b/include/swaybar/render.h | |||
@@ -1,10 +1,8 @@ | |||
1 | #ifndef _SWAYBAR_RENDER_H | 1 | #ifndef _SWAYBAR_RENDER_H |
2 | #define _SWAYBAR_RENDER_H | 2 | #define _SWAYBAR_RENDER_H |
3 | 3 | ||
4 | struct swaybar; | ||
5 | struct swaybar_output; | 4 | struct swaybar_output; |
6 | struct swaybar_config; | ||
7 | 5 | ||
8 | void render_frame(struct swaybar *bar, struct swaybar_output *output); | 6 | void render_frame(struct swaybar_output *output); |
9 | 7 | ||
10 | #endif | 8 | #endif |
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index d3eabdf6..ca88b0c5 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h | |||
@@ -13,26 +13,6 @@ enum status_protocol { | |||
13 | PROTOCOL_I3BAR, | 13 | PROTOCOL_I3BAR, |
14 | }; | 14 | }; |
15 | 15 | ||
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; |
@@ -43,6 +23,7 @@ struct status_line { | |||
43 | struct wl_list blocks; // i3bar_block::link | 23 | struct wl_list blocks; // i3bar_block::link |
44 | 24 | ||
45 | bool click_events; | 25 | bool click_events; |
26 | bool clicked; | ||
46 | char *buffer; | 27 | char *buffer; |
47 | size_t buffer_size; | 28 | size_t buffer_size; |
48 | size_t buffer_index; | 29 | size_t buffer_index; |
@@ -55,11 +36,5 @@ struct status_line *status_line_init(char *cmd); | |||
55 | void status_error(struct status_line *status, const char *text); | 36 | void status_error(struct status_line *status, const char *text); |
56 | bool status_handle_readable(struct status_line *status); | 37 | bool status_handle_readable(struct status_line *status); |
57 | void status_line_free(struct status_line *status); | 38 | 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 | 39 | ||
65 | #endif | 40 | #endif |
diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h deleted file mode 100644 index eb9cfea7..00000000 --- a/include/swaybar/tray/dbus.h +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | #ifndef _SWAYBAR_DBUS_H | ||
2 | #define _SWAYBAR_DBUS_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <dbus/dbus.h> | ||
6 | extern DBusConnection *conn; | ||
7 | |||
8 | /** | ||
9 | * Should be called in main loop to dispatch events | ||
10 | */ | ||
11 | void dispatch_dbus(); | ||
12 | |||
13 | /** | ||
14 | * Initializes async dbus communication | ||
15 | */ | ||
16 | int dbus_init(); | ||
17 | |||
18 | #endif /* _SWAYBAR_DBUS_H */ | ||
diff --git a/include/swaybar/tray/sni_watcher.h b/include/swaybar/tray/sni_watcher.h deleted file mode 100644 index 25ddfcd2..00000000 --- a/include/swaybar/tray/sni_watcher.h +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | #ifndef _SWAYBAR_SNI_WATCHER_H | ||
2 | #define _SWAYBAR_SNI_WATCHER_H | ||
3 | |||
4 | /** | ||
5 | * Starts the sni_watcher, the watcher is practically a black box and should | ||
6 | * only be accessed though functions described in its spec | ||
7 | */ | ||
8 | int init_sni_watcher(); | ||
9 | |||
10 | #endif /* _SWAYBAR_SNI_WATCHER_H */ | ||
diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h deleted file mode 100644 index 2d0662be..00000000 --- a/include/swaybar/tray/tray.h +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | #ifndef _SWAYBAR_TRAY_H | ||
2 | #define _SWAYBAR_TRAY_H | ||
3 | |||
4 | #include <stdint.h> | ||
5 | #include <stdbool.h> | ||
6 | #include "swaybar/tray/dbus.h" | ||
7 | #include "swaybar/tray/sni.h" | ||
8 | #include "swaybar/bar.h" | ||
9 | #include "list.h" | ||
10 | |||
11 | extern struct tray *tray; | ||
12 | |||
13 | struct tray { | ||
14 | list_t *items; | ||
15 | }; | ||
16 | |||
17 | /** | ||
18 | * Processes a mouse event on the bar | ||
19 | */ | ||
20 | void tray_mouse_event(struct output *output, int x, int y, | ||
21 | uint32_t button, uint32_t state); | ||
22 | |||
23 | uint32_t tray_render(struct output *output, struct config *config); | ||
24 | |||
25 | void tray_upkeep(struct bar *bar); | ||
26 | |||
27 | /** | ||
28 | * Initializes the tray with D-Bus | ||
29 | */ | ||
30 | void init_tray(struct bar *bar); | ||
31 | |||
32 | #endif /* _SWAYBAR_TRAY_H */ | ||
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 2f0cd34d..970e3cc9 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h | |||
@@ -101,5 +101,8 @@ void render_frame(struct swaylock_surface *surface); | |||
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 76eaff20..1e7ce281 100644 --- a/meson.build +++ b/meson.build | |||
@@ -42,7 +42,6 @@ pango = dependency('pango') | |||
42 | pangocairo = dependency('pangocairo') | 42 | pangocairo = dependency('pangocairo') |
43 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) | 43 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) |
44 | pixman = dependency('pixman-1') | 44 | pixman = dependency('pixman-1') |
45 | libcap = dependency('libcap', required: false) | ||
46 | libinput = dependency('libinput', version: '>=1.6.0') | 45 | libinput = dependency('libinput', version: '>=1.6.0') |
47 | libpam = cc.find_library('pam', required: false) | 46 | libpam = cc.find_library('pam', required: false) |
48 | systemd = dependency('libsystemd', required: false) | 47 | systemd = dependency('libsystemd', required: false) |
@@ -74,6 +73,11 @@ if elogind.found() | |||
74 | swayidle_deps += elogind | 73 | swayidle_deps += elogind |
75 | endif | 74 | endif |
76 | 75 | ||
76 | if not systemd.found() and not elogind.found() | ||
77 | warning('The sway binary must be setuid when compiled without (e)logind') | ||
78 | warning('You must do this manually post-install: chmod a+s /path/to/sway') | ||
79 | endif | ||
80 | |||
77 | scdoc = find_program('scdoc', required: false) | 81 | scdoc = find_program('scdoc', required: false) |
78 | 82 | ||
79 | if scdoc.found() | 83 | if scdoc.found() |
@@ -124,7 +128,16 @@ else | |||
124 | endif | 128 | endif |
125 | add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') | 129 | add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') |
126 | 130 | ||
127 | add_project_arguments('-D_LD_LIBRARY_PATH="@0@"'.format(get_option('ld-library-path')), language: 'c') | 131 | if get_option('use_rpath') |
132 | if get_option('custom_rpath') == '' | ||
133 | # default to platform specific libdir, one level up from the binary | ||
134 | rpathdir = join_paths('$ORIGIN', '..', '$LIB') | ||
135 | else | ||
136 | rpathdir = get_option('custom_rpath') | ||
137 | endif | ||
138 | else | ||
139 | rpathdir = '' | ||
140 | endif | ||
128 | 141 | ||
129 | sway_inc = include_directories('include') | 142 | sway_inc = include_directories('include') |
130 | 143 | ||
@@ -139,10 +152,7 @@ subdir('swaybg') | |||
139 | subdir('swaybar') | 152 | subdir('swaybar') |
140 | subdir('swayidle') | 153 | subdir('swayidle') |
141 | subdir('swaynag') | 154 | subdir('swaynag') |
142 | 155 | subdir('swaylock') | |
143 | if libpam.found() | ||
144 | subdir('swaylock') | ||
145 | endif | ||
146 | 156 | ||
147 | config = configuration_data() | 157 | config = configuration_data() |
148 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 158 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) |
diff --git a/meson_options.txt b/meson_options.txt index 50d646fd..2db852fc 100644 --- a/meson_options.txt +++ b/meson_options.txt | |||
@@ -1,5 +1,6 @@ | |||
1 | option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.') | 1 | option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.') |
2 | option('ld-library-path', type: 'string', value: '', description: 'The LD_LIBRARY_PATH environment variable.') | 2 | option('use_rpath', type: 'boolean', value: false, description: 'install binaries with rpath set') |
3 | option('custom_rpath', type: 'string', value: '', description: 'override rpath with a custom one') | ||
3 | option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') | 4 | option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') |
4 | option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') | 5 | option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') |
5 | option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') | 6 | option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') |
diff --git a/sway/commands.c b/sway/commands.c index bff230f7..72db8ab9 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -110,6 +110,7 @@ static struct cmd_handler handlers[] = { | |||
110 | { "seat", cmd_seat }, | 110 | { "seat", cmd_seat }, |
111 | { "set", cmd_set }, | 111 | { "set", cmd_set }, |
112 | { "show_marks", cmd_show_marks }, | 112 | { "show_marks", cmd_show_marks }, |
113 | { "smart_borders", cmd_smart_borders }, | ||
113 | { "smart_gaps", cmd_smart_gaps }, | 114 | { "smart_gaps", cmd_smart_gaps }, |
114 | { "tiling_drag", cmd_tiling_drag }, | 115 | { "tiling_drag", cmd_tiling_drag }, |
115 | { "workspace", cmd_workspace }, | 116 | { "workspace", cmd_workspace }, |
@@ -391,14 +392,16 @@ struct cmd_results *config_command(char *exec) { | |||
391 | // Var replacement, for all but first argument of set | 392 | // Var replacement, for all but first argument of set |
392 | // TODO commands | 393 | // TODO commands |
393 | for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { | 394 | for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { |
395 | if (handler->handle != cmd_exec && handler->handle != cmd_exec_always | ||
396 | && handler->handle != cmd_bindsym | ||
397 | && handler->handle != cmd_bindcode | ||
398 | && handler->handle != cmd_set | ||
399 | && (*argv[i] == '\"' || *argv[i] == '\'')) { | ||
400 | strip_quotes(argv[i]); | ||
401 | } | ||
394 | argv[i] = do_var_replacement(argv[i]); | 402 | argv[i] = do_var_replacement(argv[i]); |
395 | unescape_string(argv[i]); | 403 | unescape_string(argv[i]); |
396 | } | 404 | } |
397 | // Strip quotes for first argument. | ||
398 | // TODO This part needs to be handled much better | ||
399 | if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { | ||
400 | strip_quotes(argv[1]); | ||
401 | } | ||
402 | if (handler->handle) { | 405 | if (handler->handle) { |
403 | results = handler->handle(argc-1, argv+1); | 406 | results = handler->handle(argc-1, argv+1); |
404 | } else { | 407 | } else { |
@@ -422,11 +425,6 @@ struct cmd_results *config_subcommand(char **argv, int argc, | |||
422 | char *input = argv[0] ? argv[0] : "(empty)"; | 425 | char *input = argv[0] ? argv[0] : "(empty)"; |
423 | return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); | 426 | return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); |
424 | } | 427 | } |
425 | // Strip quotes for first argument. | ||
426 | // TODO This part needs to be handled much better | ||
427 | if (argc > 1 && (*argv[1] == '\"' || *argv[1] == '\'')) { | ||
428 | strip_quotes(argv[1]); | ||
429 | } | ||
430 | if (handler->handle) { | 428 | if (handler->handle) { |
431 | return handler->handle(argc - 1, argv + 1); | 429 | return handler->handle(argc - 1, argv + 1); |
432 | } | 430 | } |
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index 0c48bee9..f18b8d7c 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c | |||
@@ -21,7 +21,9 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { | |||
21 | config->current_bar->binding_mode_indicator = false; | 21 | config->current_bar->binding_mode_indicator = false; |
22 | wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", | 22 | wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", |
23 | config->current_bar->id); | 23 | config->current_bar->id); |
24 | } else { | ||
25 | return cmd_results_new(CMD_INVALID, "binding_mode_indicator", | ||
26 | "Invalid value %s", argv[0]); | ||
24 | } | 27 | } |
25 | return cmd_results_new(CMD_INVALID, "binding_mode_indicator", | 28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
26 | "Invalid value %s", argv[0]); | ||
27 | } | 29 | } |
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index d676e475..2e0876a9 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <strings.h> | ||
2 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 4 | #include "sway/config.h" |
4 | #include "sway/tree/arrange.h" | 5 | #include "sway/tree/arrange.h" |
@@ -13,172 +14,173 @@ enum gaps_op { | |||
13 | GAPS_OP_SUBTRACT | 14 | GAPS_OP_SUBTRACT |
14 | }; | 15 | }; |
15 | 16 | ||
16 | enum gaps_scope { | 17 | struct gaps_data { |
17 | GAPS_SCOPE_ALL, | 18 | bool inner; |
18 | GAPS_SCOPE_WORKSPACE, | 19 | enum gaps_op operation; |
19 | GAPS_SCOPE_CURRENT | 20 | int amount; |
20 | }; | 21 | }; |
21 | 22 | ||
22 | struct cmd_results *cmd_gaps(int argc, char **argv) { | 23 | // gaps edge_gaps on|off|toggle |
23 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1); | 24 | static struct cmd_results *gaps_edge_gaps(int argc, char **argv) { |
24 | if (error) { | 25 | struct cmd_results *error; |
26 | if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { | ||
25 | return error; | 27 | return error; |
26 | } | 28 | } |
27 | 29 | ||
28 | if (strcmp(argv[0], "edge_gaps") == 0) { | 30 | if (strcmp(argv[1], "on") == 0) { |
29 | if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2))) { | 31 | config->edge_gaps = true; |
30 | return error; | 32 | } else if (strcmp(argv[1], "off") == 0) { |
31 | } | 33 | config->edge_gaps = false; |
32 | 34 | } else if (strcmp(argv[1], "toggle") == 0) { | |
33 | if (strcmp(argv[1], "on") == 0) { | 35 | if (!config->active) { |
34 | config->edge_gaps = true; | ||
35 | } else if (strcmp(argv[1], "off") == 0) { | ||
36 | config->edge_gaps = false; | ||
37 | } else if (strcmp(argv[1], "toggle") == 0) { | ||
38 | if (!config->active) { | ||
39 | return cmd_results_new(CMD_INVALID, "gaps", | ||
40 | "Cannot toggle gaps while not running."); | ||
41 | } | ||
42 | config->edge_gaps = !config->edge_gaps; | ||
43 | } else { | ||
44 | return cmd_results_new(CMD_INVALID, "gaps", | 36 | return cmd_results_new(CMD_INVALID, "gaps", |
45 | "gaps edge_gaps on|off|toggle"); | 37 | "Cannot toggle gaps while not running."); |
46 | } | 38 | } |
47 | arrange_root(); | 39 | config->edge_gaps = !config->edge_gaps; |
48 | } else { | 40 | } else { |
49 | int amount_idx = 0; // the current index in argv | 41 | return cmd_results_new(CMD_INVALID, "gaps", |
50 | enum gaps_op op = GAPS_OP_SET; | 42 | "gaps edge_gaps on|off|toggle"); |
51 | enum gaps_scope scope = GAPS_SCOPE_ALL; | 43 | } |
52 | bool inner = true; | 44 | arrange_root(); |
53 | 45 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | |
54 | if (strcmp(argv[0], "inner") == 0) { | 46 | } |
55 | amount_idx++; | ||
56 | inner = true; | ||
57 | } else if (strcmp(argv[0], "outer") == 0) { | ||
58 | amount_idx++; | ||
59 | inner = false; | ||
60 | } | ||
61 | 47 | ||
62 | // If one of the long variants of the gaps command is used | 48 | // gaps inner|outer <px> |
63 | // (which starts with inner|outer) check the number of args | 49 | static struct cmd_results *gaps_set_defaults(int argc, char **argv) { |
64 | if (amount_idx > 0) { // if we've seen inner|outer | 50 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); |
65 | if (argc > 2) { // check the longest variant | 51 | if (error) { |
66 | error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); | 52 | return error; |
67 | if (error) { | 53 | } |
68 | return error; | ||
69 | } | ||
70 | } else { // check the next longest format | ||
71 | error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); | ||
72 | if (error) { | ||
73 | return error; | ||
74 | } | ||
75 | } | ||
76 | } else { | ||
77 | error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 1); | ||
78 | if (error) { | ||
79 | return error; | ||
80 | } | ||
81 | } | ||
82 | 54 | ||
83 | if (argc == 4) { | 55 | bool inner; |
84 | // Long format: all|workspace|current. | 56 | if (strcasecmp(argv[0], "inner") == 0) { |
85 | if (strcmp(argv[amount_idx], "all") == 0) { | 57 | inner = true; |
86 | amount_idx++; | 58 | } else if (strcasecmp(argv[0], "outer") == 0) { |
87 | scope = GAPS_SCOPE_ALL; | 59 | inner = false; |
88 | } else if (strcmp(argv[amount_idx], "workspace") == 0) { | 60 | } else { |
89 | amount_idx++; | 61 | return cmd_results_new(CMD_INVALID, "gaps", |
90 | scope = GAPS_SCOPE_WORKSPACE; | 62 | "Expected 'gaps inner|outer <px>'"); |
91 | } else if (strcmp(argv[amount_idx], "current") == 0) { | 63 | } |
92 | amount_idx++; | ||
93 | scope = GAPS_SCOPE_CURRENT; | ||
94 | } | ||
95 | |||
96 | // Long format: set|plus|minus | ||
97 | if (strcmp(argv[amount_idx], "set") == 0) { | ||
98 | amount_idx++; | ||
99 | op = GAPS_OP_SET; | ||
100 | } else if (strcmp(argv[amount_idx], "plus") == 0) { | ||
101 | amount_idx++; | ||
102 | op = GAPS_OP_ADD; | ||
103 | } else if (strcmp(argv[amount_idx], "minus") == 0) { | ||
104 | amount_idx++; | ||
105 | op = GAPS_OP_SUBTRACT; | ||
106 | } | ||
107 | } | ||
108 | 64 | ||
109 | char *end; | 65 | char *end; |
110 | double val = strtod(argv[amount_idx], &end); | 66 | int amount = strtol(argv[1], &end, 10); |
111 | 67 | if (strlen(end) && strcasecmp(end, "px") != 0) { | |
112 | if (strlen(end) && val == 0.0) { // invalid <amount> | 68 | return cmd_results_new(CMD_INVALID, "gaps", |
113 | // guess which variant of the command was attempted | 69 | "Expected 'gaps inner|outer <px>'"); |
114 | if (argc == 1) { | 70 | } |
115 | return cmd_results_new(CMD_INVALID, "gaps", "gaps <amount>"); | 71 | if (amount < 0) { |
116 | } | 72 | amount = 0; |
117 | if (argc == 2) { | 73 | } |
118 | return cmd_results_new(CMD_INVALID, "gaps", | ||
119 | "gaps inner|outer <amount>"); | ||
120 | } | ||
121 | return cmd_results_new(CMD_INVALID, "gaps", | ||
122 | "gaps inner|outer all|workspace|current set|plus|minus <amount>"); | ||
123 | } | ||
124 | 74 | ||
125 | if (amount_idx == 0) { // gaps <amount> | 75 | if (inner) { |
126 | config->gaps_inner = val; | 76 | config->gaps_inner = amount; |
127 | config->gaps_outer = val; | 77 | } else { |
128 | arrange_root(); | 78 | config->gaps_outer = amount; |
129 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 79 | } |
130 | } | 80 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
131 | // Other variants. The middle-length variant (gaps inner|outer <amount>) | 81 | } |
132 | // just defaults the scope to "all" and defaults the op to "set". | ||
133 | |||
134 | double total; | ||
135 | switch (op) { | ||
136 | case GAPS_OP_SUBTRACT: { | ||
137 | total = (inner ? config->gaps_inner : config->gaps_outer) - val; | ||
138 | if (total < 0) { | ||
139 | total = 0; | ||
140 | } | ||
141 | break; | ||
142 | } | ||
143 | case GAPS_OP_ADD: { | ||
144 | total = (inner ? config->gaps_inner : config->gaps_outer) + val; | ||
145 | break; | ||
146 | } | ||
147 | case GAPS_OP_SET: { | ||
148 | total = val; | ||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | 82 | ||
153 | if (scope == GAPS_SCOPE_ALL) { | 83 | static void configure_gaps(struct sway_workspace *ws, void *_data) { |
154 | if (inner) { | 84 | struct gaps_data *data = _data; |
155 | config->gaps_inner = total; | 85 | int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer; |
156 | } else { | 86 | |
157 | config->gaps_outer = total; | 87 | switch (data->operation) { |
158 | } | 88 | case GAPS_OP_SET: |
159 | arrange_root(); | 89 | *prop = data->amount; |
160 | } else { | 90 | break; |
161 | if (scope == GAPS_SCOPE_WORKSPACE) { | 91 | case GAPS_OP_ADD: |
162 | struct sway_workspace *ws = config->handler_context.workspace; | 92 | *prop += data->amount; |
163 | ws->has_gaps = true; | 93 | break; |
164 | if (inner) { | 94 | case GAPS_OP_SUBTRACT: |
165 | ws->gaps_inner = total; | 95 | *prop -= data->amount; |
166 | } else { | 96 | break; |
167 | ws->gaps_outer = total; | 97 | } |
168 | } | 98 | if (*prop < 0) { |
169 | arrange_workspace(ws); | 99 | *prop = 0; |
170 | } else { | 100 | } |
171 | struct sway_container *c = config->handler_context.container; | 101 | arrange_workspace(ws); |
172 | c->has_gaps = true; | 102 | } |
173 | if (inner) { | 103 | |
174 | c->gaps_inner = total; | 104 | // gaps inner|outer current|all set|plus|minus <px> |
175 | } else { | 105 | static struct cmd_results *gaps_set_runtime(int argc, char **argv) { |
176 | c->gaps_outer = total; | 106 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); |
177 | } | 107 | if (error) { |
178 | arrange_workspace(c->workspace); | 108 | return error; |
179 | } | 109 | } |
180 | } | 110 | |
111 | struct gaps_data data; | ||
112 | |||
113 | if (strcasecmp(argv[0], "inner") == 0) { | ||
114 | data.inner = true; | ||
115 | } else if (strcasecmp(argv[0], "outer") == 0) { | ||
116 | data.inner = false; | ||
117 | } else { | ||
118 | return cmd_results_new(CMD_INVALID, "gaps", | ||
119 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | ||
120 | } | ||
121 | |||
122 | bool all; | ||
123 | if (strcasecmp(argv[1], "current") == 0) { | ||
124 | all = false; | ||
125 | } else if (strcasecmp(argv[1], "all") == 0) { | ||
126 | all = true; | ||
127 | } else { | ||
128 | return cmd_results_new(CMD_INVALID, "gaps", | ||
129 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | ||
130 | } | ||
131 | |||
132 | if (strcasecmp(argv[2], "set") == 0) { | ||
133 | data.operation = GAPS_OP_SET; | ||
134 | } else if (strcasecmp(argv[2], "plus") == 0) { | ||
135 | data.operation = GAPS_OP_ADD; | ||
136 | } else if (strcasecmp(argv[2], "minus") == 0) { | ||
137 | data.operation = GAPS_OP_SUBTRACT; | ||
138 | } else { | ||
139 | return cmd_results_new(CMD_INVALID, "gaps", | ||
140 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | ||
141 | } | ||
142 | |||
143 | char *end; | ||
144 | data.amount = strtol(argv[3], &end, 10); | ||
145 | if (strlen(end) && strcasecmp(end, "px") != 0) { | ||
146 | return cmd_results_new(CMD_INVALID, "gaps", | ||
147 | "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); | ||
148 | } | ||
149 | |||
150 | if (all) { | ||
151 | root_for_each_workspace(configure_gaps, &data); | ||
152 | } else { | ||
153 | configure_gaps(config->handler_context.workspace, &data); | ||
181 | } | 154 | } |
182 | 155 | ||
183 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 156 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
184 | } | 157 | } |
158 | |||
159 | // gaps edge_gaps on|off|toggle | ||
160 | // gaps inner|outer <px> - sets defaults for workspaces | ||
161 | // gaps inner|outer current|all set|plus|minus <px> - runtime only | ||
162 | struct cmd_results *cmd_gaps(int argc, char **argv) { | ||
163 | struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); | ||
164 | if (error) { | ||
165 | return error; | ||
166 | } | ||
167 | |||
168 | if (strcmp(argv[0], "edge_gaps") == 0) { | ||
169 | return gaps_edge_gaps(argc, argv); | ||
170 | } | ||
171 | |||
172 | if (argc == 2) { | ||
173 | return gaps_set_defaults(argc, argv); | ||
174 | } | ||
175 | if (argc == 4) { | ||
176 | if (config->active) { | ||
177 | return gaps_set_runtime(argc, argv); | ||
178 | } else { | ||
179 | return cmd_results_new(CMD_INVALID, "gaps", | ||
180 | "This syntax can only be used when sway is running"); | ||
181 | } | ||
182 | } | ||
183 | return cmd_results_new(CMD_INVALID, "gaps", | ||
184 | "Expected 'gaps inner|outer <px>' or " | ||
185 | "'gaps inner|outer current|all set|plus|minus <px>'"); | ||
186 | } | ||
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 0a5c7f28..ea261fb1 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c | |||
@@ -1,15 +1,8 @@ | |||
1 | #include "sway/commands.h" | 1 | #include "sway/commands.h" |
2 | #include "sway/config.h" | 2 | #include "sway/config.h" |
3 | #include "sway/tree/container.h" | 3 | #include "sway/tree/arrange.h" |
4 | #include "sway/tree/root.h" | ||
5 | #include "sway/tree/view.h" | 4 | #include "sway/tree/view.h" |
6 | 5 | ||
7 | static void _configure_view(struct sway_container *con, void *data) { | ||
8 | if (con->view) { | ||
9 | view_autoconfigure(con->view); | ||
10 | } | ||
11 | } | ||
12 | |||
13 | struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | 6 | struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { |
14 | struct cmd_results *error = NULL; | 7 | struct cmd_results *error = NULL; |
15 | if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { |
@@ -26,13 +19,16 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { | |||
26 | config->hide_edge_borders = E_BOTH; | 19 | config->hide_edge_borders = E_BOTH; |
27 | } else if (strcmp(argv[0], "smart") == 0) { | 20 | } else if (strcmp(argv[0], "smart") == 0) { |
28 | config->hide_edge_borders = E_SMART; | 21 | config->hide_edge_borders = E_SMART; |
22 | } else if (strcmp(argv[0], "smart_no_gaps") == 0) { | ||
23 | config->hide_edge_borders = E_SMART_NO_GAPS; | ||
29 | } else { | 24 | } else { |
30 | return cmd_results_new(CMD_INVALID, "hide_edge_borders", | 25 | return cmd_results_new(CMD_INVALID, "hide_edge_borders", |
31 | "Expected 'hide_edge_borders " | 26 | "Expected 'hide_edge_borders " |
32 | "<none|vertical|horizontal|both|smart>'"); | 27 | "<none|vertical|horizontal|both|smart|smart_no_gaps>'"); |
33 | } | 28 | } |
29 | config->saved_edge_borders = config->hide_edge_borders; | ||
34 | 30 | ||
35 | root_for_each_container(_configure_view, NULL); | 31 | arrange_root(); |
36 | 32 | ||
37 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 33 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
38 | } | 34 | } |
diff --git a/sway/commands/input.c b/sway/commands/input.c index 9091da2a..2889d47d 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -9,6 +9,7 @@ | |||
9 | static struct cmd_handler input_handlers[] = { | 9 | static struct cmd_handler input_handlers[] = { |
10 | { "accel_profile", input_cmd_accel_profile }, | 10 | { "accel_profile", input_cmd_accel_profile }, |
11 | { "click_method", input_cmd_click_method }, | 11 | { "click_method", input_cmd_click_method }, |
12 | { "drag", input_cmd_drag }, | ||
12 | { "drag_lock", input_cmd_drag_lock }, | 13 | { "drag_lock", input_cmd_drag_lock }, |
13 | { "dwt", input_cmd_dwt }, | 14 | { "dwt", input_cmd_dwt }, |
14 | { "events", input_cmd_events }, | 15 | { "events", input_cmd_events }, |
diff --git a/sway/commands/input/drag.c b/sway/commands/input/drag.c new file mode 100644 index 00000000..e325df29 --- /dev/null +++ b/sway/commands/input/drag.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | #include "util.h" | ||
7 | |||
8 | struct cmd_results *input_cmd_drag(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "drag", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | struct input_config *ic = config->handler_context.input_config; | ||
14 | if (!ic) { | ||
15 | return cmd_results_new(CMD_FAILURE, | ||
16 | "drag", "No input device defined."); | ||
17 | } | ||
18 | |||
19 | if (parse_boolean(argv[0], true)) { | ||
20 | ic->drag = LIBINPUT_CONFIG_DRAG_ENABLED; | ||
21 | } else { | ||
22 | ic->drag = LIBINPUT_CONFIG_DRAG_DISABLED; | ||
23 | } | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index ef3ec1cb..c2ce2e78 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -138,15 +138,14 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
138 | } | 138 | } |
139 | container->layout = new_layout; | 139 | container->layout = new_layout; |
140 | container_update_representation(container); | 140 | container_update_representation(container); |
141 | arrange_container(container); | ||
142 | } else { | 141 | } else { |
143 | if (old_layout != L_TABBED && old_layout != L_STACKED) { | 142 | if (old_layout != L_TABBED && old_layout != L_STACKED) { |
144 | workspace->prev_split_layout = old_layout; | 143 | workspace->prev_split_layout = old_layout; |
145 | } | 144 | } |
146 | workspace->layout = new_layout; | 145 | workspace->layout = new_layout; |
147 | workspace_update_representation(workspace); | 146 | workspace_update_representation(workspace); |
148 | arrange_workspace(workspace); | ||
149 | } | 147 | } |
148 | arrange_workspace(workspace); | ||
150 | } | 149 | } |
151 | 150 | ||
152 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 151 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 9e370d43..30fb47c4 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c | |||
@@ -123,19 +123,13 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { | |||
123 | } | 123 | } |
124 | free(src); | 124 | free(src); |
125 | } else { | 125 | } else { |
126 | // Escape spaces and quotes in the final path for swaybg | 126 | // Escape double quotes in the final path for swaybg |
127 | for (size_t i = 0; i < strlen(src); i++) { | 127 | for (size_t i = 0; i < strlen(src); i++) { |
128 | switch (src[i]) { | 128 | if (src[i] == '"') { |
129 | case ' ': | 129 | src = realloc(src, strlen(src) + 2); |
130 | case '\'': | 130 | memmove(src + i + 1, src + i, strlen(src + i) + 1); |
131 | case '\"': | 131 | *(src + i) = '\\'; |
132 | src = realloc(src, strlen(src) + 2); | 132 | i++; |
133 | memmove(src + i + 1, src + i, strlen(src + i) + 1); | ||
134 | *(src + i) = '\\'; | ||
135 | i++; | ||
136 | break; | ||
137 | default: | ||
138 | break; | ||
139 | } | 133 | } |
140 | } | 134 | } |
141 | 135 | ||
diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 99e9dbda..1343b165 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c | |||
@@ -179,11 +179,11 @@ static void container_recursive_resize(struct sway_container *container, | |||
179 | } | 179 | } |
180 | } | 180 | } |
181 | 181 | ||
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/smart_borders.c b/sway/commands/smart_borders.c new file mode 100644 index 00000000..fcb4040e --- /dev/null +++ b/sway/commands/smart_borders.c | |||
@@ -0,0 +1,25 @@ | |||
1 | #include "sway/commands.h" | ||
2 | #include "sway/config.h" | ||
3 | #include "sway/tree/arrange.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | struct cmd_results *cmd_smart_borders(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "smart_borders", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | enum edge_border_types saved = config->hide_edge_borders; | ||
14 | if (strcmp(argv[0], "no_gaps") == 0) { | ||
15 | config->hide_edge_borders = E_SMART_NO_GAPS; | ||
16 | } else { | ||
17 | config->hide_edge_borders = parse_boolean(argv[0], true) ? | ||
18 | E_SMART : config->saved_edge_borders; | ||
19 | } | ||
20 | config->saved_edge_borders = saved; | ||
21 | |||
22 | arrange_root(); | ||
23 | |||
24 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
25 | } | ||
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index f026a39d..63f29641 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c | |||
@@ -10,6 +10,28 @@ | |||
10 | #include "log.h" | 10 | #include "log.h" |
11 | #include "stringop.h" | 11 | #include "stringop.h" |
12 | 12 | ||
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 | wsc->gaps_inner = -1; | ||
24 | wsc->gaps_outer = -1; | ||
25 | list_add(config->workspace_configs, wsc); | ||
26 | return wsc; | ||
27 | } | ||
28 | |||
29 | void free_workspace_config(struct workspace_config *wsc) { | ||
30 | free(wsc->workspace); | ||
31 | free(wsc->output); | ||
32 | free(wsc); | ||
33 | } | ||
34 | |||
13 | struct cmd_results *cmd_workspace(int argc, char **argv) { | 35 | struct cmd_results *cmd_workspace(int argc, char **argv) { |
14 | struct cmd_results *error = NULL; | 36 | struct cmd_results *error = NULL; |
15 | if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { | 37 | if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { |
@@ -17,6 +39,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
17 | } | 39 | } |
18 | 40 | ||
19 | int output_location = -1; | 41 | int output_location = -1; |
42 | int gaps_location = -1; | ||
20 | 43 | ||
21 | for (int i = 0; i < argc; ++i) { | 44 | for (int i = 0; i < argc; ++i) { |
22 | if (strcasecmp(argv[i], "output") == 0) { | 45 | if (strcasecmp(argv[i], "output") == 0) { |
@@ -24,25 +47,54 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { | |||
24 | break; | 47 | break; |
25 | } | 48 | } |
26 | } | 49 | } |
50 | for (int i = 0; i < argc; ++i) { | ||
51 | if (strcasecmp(argv[i], "gaps") == 0) { | ||
52 | gaps_location = i; | ||
53 | break; | ||
54 | } | ||
55 | } | ||
27 | if (output_location >= 0) { | 56 | if (output_location >= 0) { |
28 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { | 57 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { |
29 | return error; | 58 | return error; |
30 | } | 59 | } |
31 | struct workspace_output *wso = calloc(1, sizeof(struct workspace_output)); | 60 | char *ws_name = join_args(argv, argc - 2); |
32 | if (!wso) { | 61 | struct workspace_config *wsc = workspace_config_find_or_create(ws_name); |
62 | free(ws_name); | ||
63 | if (!wsc) { | ||
33 | return cmd_results_new(CMD_FAILURE, "workspace output", | 64 | return cmd_results_new(CMD_FAILURE, "workspace output", |
34 | "Unable to allocate workspace output"); | 65 | "Unable to allocate workspace output"); |
35 | } | 66 | } |
36 | wso->workspace = join_args(argv, argc - 2); | 67 | free(wsc->output); |
37 | wso->output = strdup(argv[output_location + 1]); | 68 | wsc->output = strdup(argv[output_location + 1]); |
38 | int i = -1; | 69 | } else if (gaps_location >= 0) { |
39 | if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) { | 70 | if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { |
40 | struct workspace_output *old = config->workspace_outputs->items[i]; | 71 | return error; |
41 | free(old); // workspaces can only be assigned to a single output | 72 | } |
42 | list_del(config->workspace_outputs, i); | 73 | char *ws_name = join_args(argv, argc - 3); |
74 | struct workspace_config *wsc = workspace_config_find_or_create(ws_name); | ||
75 | free(ws_name); | ||
76 | if (!wsc) { | ||
77 | return cmd_results_new(CMD_FAILURE, "workspace gaps", | ||
78 | "Unable to allocate workspace output"); | ||
79 | } | ||
80 | int *prop = NULL; | ||
81 | if (strcasecmp(argv[gaps_location + 1], "inner") == 0) { | ||
82 | prop = &wsc->gaps_inner; | ||
83 | } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) { | ||
84 | prop = &wsc->gaps_outer; | ||
85 | } else { | ||
86 | return cmd_results_new(CMD_FAILURE, "workspace gaps", | ||
87 | "Expected 'workspace <ws> gaps inner|outer <px>'"); | ||
88 | } | ||
89 | char *end; | ||
90 | int val = strtol(argv[gaps_location + 2], &end, 10); | ||
91 | |||
92 | if (strlen(end)) { | ||
93 | free(end); | ||
94 | return cmd_results_new(CMD_FAILURE, "workspace gaps", | ||
95 | "Expected 'workspace <ws> gaps inner|outer <px>'"); | ||
43 | } | 96 | } |
44 | wlr_log(WLR_DEBUG, "Assigning workspace %s to output %s", wso->workspace, wso->output); | 97 | *prop = val >= 0 ? val : 0; |
45 | list_add(config->workspace_outputs, wso); | ||
46 | } else { | 98 | } else { |
47 | if (config->reading || !config->active) { | 99 | if (config->reading || !config->active) { |
48 | return cmd_results_new(CMD_DEFER, "workspace", NULL); | 100 | return cmd_results_new(CMD_DEFER, "workspace", NULL); |
diff --git a/sway/config.c b/sway/config.c index 830fb65f..048b57de 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -95,7 +95,12 @@ void free_config(struct sway_config *config) { | |||
95 | list_free(config->bars); | 95 | list_free(config->bars); |
96 | } | 96 | } |
97 | list_free(config->cmd_queue); | 97 | list_free(config->cmd_queue); |
98 | list_free(config->workspace_outputs); | 98 | if (config->workspace_configs) { |
99 | for (int i = 0; i < config->workspace_configs->length; i++) { | ||
100 | free_workspace_config(config->workspace_configs->items[i]); | ||
101 | } | ||
102 | list_free(config->workspace_configs); | ||
103 | } | ||
99 | if (config->output_configs) { | 104 | if (config->output_configs) { |
100 | for (int i = 0; i < config->output_configs->length; i++) { | 105 | for (int i = 0; i < config->output_configs->length; i++) { |
101 | free_output_config(config->output_configs->items[i]); | 106 | free_output_config(config->output_configs->items[i]); |
@@ -175,7 +180,7 @@ static void config_defaults(struct sway_config *config) { | |||
175 | if (!(config->symbols = create_list())) goto cleanup; | 180 | if (!(config->symbols = create_list())) goto cleanup; |
176 | if (!(config->modes = create_list())) goto cleanup; | 181 | if (!(config->modes = create_list())) goto cleanup; |
177 | if (!(config->bars = create_list())) goto cleanup; | 182 | if (!(config->bars = create_list())) goto cleanup; |
178 | if (!(config->workspace_outputs = create_list())) goto cleanup; | 183 | if (!(config->workspace_configs = create_list())) goto cleanup; |
179 | if (!(config->criteria = create_list())) goto cleanup; | 184 | if (!(config->criteria = create_list())) goto cleanup; |
180 | if (!(config->no_focus = create_list())) goto cleanup; | 185 | if (!(config->no_focus = create_list())) goto cleanup; |
181 | if (!(config->input_configs = create_list())) goto cleanup; | 186 | if (!(config->input_configs = create_list())) goto cleanup; |
@@ -244,6 +249,7 @@ static void config_defaults(struct sway_config *config) { | |||
244 | config->border_thickness = 2; | 249 | config->border_thickness = 2; |
245 | config->floating_border_thickness = 2; | 250 | config->floating_border_thickness = 2; |
246 | config->hide_edge_borders = E_NONE; | 251 | config->hide_edge_borders = E_NONE; |
252 | config->saved_edge_borders = E_NONE; | ||
247 | 253 | ||
248 | // border colors | 254 | // border colors |
249 | set_color(config->border_colors.focused.border, 0x4C7899); | 255 | set_color(config->border_colors.focused.border, 0x4C7899); |
@@ -804,7 +810,7 @@ char *do_var_replacement(char *str) { | |||
804 | // would compare two structs in full, while this method only compares the | 810 | // would compare two structs in full, while this method only compares the |
805 | // workspace. | 811 | // workspace. |
806 | int workspace_output_cmp_workspace(const void *a, const void *b) { | 812 | int workspace_output_cmp_workspace(const void *a, const void *b) { |
807 | const struct workspace_output *wsa = a, *wsb = b; | 813 | const struct workspace_config *wsa = a, *wsb = b; |
808 | return lenient_strcmp(wsa->workspace, wsb->workspace); | 814 | return lenient_strcmp(wsa->workspace, wsb->workspace); |
809 | } | 815 | } |
810 | 816 | ||
diff --git a/sway/config/input.c b/sway/config/input.c index 6b43a5b9..794d5194 100644 --- a/sway/config/input.c +++ b/sway/config/input.c | |||
@@ -20,6 +20,7 @@ struct input_config *new_input_config(const char* identifier) { | |||
20 | 20 | ||
21 | input->tap = INT_MIN; | 21 | input->tap = INT_MIN; |
22 | input->tap_button_map = INT_MIN; | 22 | input->tap_button_map = INT_MIN; |
23 | input->drag = INT_MIN; | ||
23 | input->drag_lock = INT_MIN; | 24 | input->drag_lock = INT_MIN; |
24 | input->dwt = INT_MIN; | 25 | input->dwt = INT_MIN; |
25 | input->send_events = INT_MIN; | 26 | input->send_events = INT_MIN; |
@@ -46,6 +47,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
46 | if (src->click_method != INT_MIN) { | 47 | if (src->click_method != INT_MIN) { |
47 | dst->click_method = src->click_method; | 48 | dst->click_method = src->click_method; |
48 | } | 49 | } |
50 | if (src->drag != INT_MIN) { | ||
51 | dst->drag = src->drag; | ||
52 | } | ||
49 | if (src->drag_lock != INT_MIN) { | 53 | if (src->drag_lock != INT_MIN) { |
50 | dst->drag_lock = src->drag_lock; | 54 | dst->drag_lock = src->drag_lock; |
51 | } | 55 | } |
diff --git a/sway/config/output.c b/sway/config/output.c index 74d79130..6f337b66 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -237,7 +237,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
237 | wlr_log(WLR_DEBUG, "Setting background for output %d to %s", | 237 | wlr_log(WLR_DEBUG, "Setting background for output %d to %s", |
238 | output_i, oc->background); | 238 | output_i, oc->background); |
239 | 239 | ||
240 | size_t len = snprintf(NULL, 0, "%s %d %s %s %s", | 240 | size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s", |
241 | config->swaybg_command ? config->swaybg_command : "swaybg", | 241 | config->swaybg_command ? config->swaybg_command : "swaybg", |
242 | output_i, oc->background, oc->background_option, | 242 | output_i, oc->background, oc->background_option, |
243 | oc->background_fallback ? oc->background_fallback : ""); | 243 | oc->background_fallback ? oc->background_fallback : ""); |
@@ -246,7 +246,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { | |||
246 | wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); | 246 | wlr_log(WLR_DEBUG, "Unable to allocate swaybg command"); |
247 | return; | 247 | return; |
248 | } | 248 | } |
249 | snprintf(command, len + 1, "%s %d %s %s %s", | 249 | snprintf(command, len + 1, "%s %d \"%s\" %s %s", |
250 | config->swaybg_command ? config->swaybg_command : "swaybg", | 250 | config->swaybg_command ? config->swaybg_command : "swaybg", |
251 | output_i, oc->background, oc->background_option, | 251 | output_i, oc->background, oc->background_option, |
252 | oc->background_fallback ? oc->background_fallback : ""); | 252 | oc->background_fallback ? oc->background_fallback : ""); |
diff --git a/sway/config/seat.c b/sway/config/seat.c index 83dac4c0..46456caf 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -30,7 +30,7 @@ struct seat_config *new_seat_config(const char* name) { | |||
30 | return seat; | 30 | return seat; |
31 | } | 31 | } |
32 | 32 | ||
33 | struct seat_attachment_config *seat_attachment_config_new() { | 33 | struct seat_attachment_config *seat_attachment_config_new(void) { |
34 | struct seat_attachment_config *attachment = | 34 | struct seat_attachment_config *attachment = |
35 | calloc(1, sizeof(struct seat_attachment_config)); | 35 | calloc(1, sizeof(struct seat_attachment_config)); |
36 | if (!attachment) { | 36 | if (!attachment) { |
diff --git a/sway/criteria.c b/sway/criteria.c index 0193233e..575e8bcf 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -339,6 +339,10 @@ static enum criteria_token token_from_name(char *name) { | |||
339 | return T_URGENT; | 339 | return T_URGENT; |
340 | } else if (strcmp(name, "workspace") == 0) { | 340 | } else if (strcmp(name, "workspace") == 0) { |
341 | return T_WORKSPACE; | 341 | return T_WORKSPACE; |
342 | } else if (strcmp(name, "tiling") == 0) { | ||
343 | return T_TILING; | ||
344 | } else if (strcmp(name, "floating") == 0) { | ||
345 | return T_FLOATING; | ||
342 | } | 346 | } |
343 | return T_INVALID; | 347 | return T_INVALID; |
344 | } | 348 | } |
diff --git a/sway/debug-tree.c b/sway/debug-tree.c index 9644f4e5..16b479f9 100644 --- a/sway/debug-tree.c +++ b/sway/debug-tree.c | |||
@@ -120,7 +120,7 @@ static int draw_node(cairo_t *cairo, struct sway_node *node, | |||
120 | return height; | 120 | return height; |
121 | } | 121 | } |
122 | 122 | ||
123 | void update_debug_tree() { | 123 | void update_debug_tree(void) { |
124 | if (!debug.render_tree) { | 124 | if (!debug.render_tree) { |
125 | return; | 125 | return; |
126 | } | 126 | } |
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 4624d824..e717ee35 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -31,14 +31,14 @@ struct sway_transaction_instruction { | |||
31 | struct sway_transaction *transaction; | 31 | struct sway_transaction *transaction; |
32 | struct sway_node *node; | 32 | struct sway_node *node; |
33 | union { | 33 | union { |
34 | struct sway_output_state *output_state; | 34 | struct sway_output_state output_state; |
35 | struct sway_workspace_state *workspace_state; | 35 | struct sway_workspace_state workspace_state; |
36 | struct sway_container_state *container_state; | 36 | struct sway_container_state container_state; |
37 | }; | 37 | }; |
38 | uint32_t serial; | 38 | uint32_t serial; |
39 | }; | 39 | }; |
40 | 40 | ||
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; |
@@ -300,15 +281,15 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
300 | case N_ROOT: | 281 | case N_ROOT: |
301 | break; | 282 | break; |
302 | case N_OUTPUT: | 283 | case N_OUTPUT: |
303 | apply_output_state(node->sway_output, instruction->output_state); | 284 | apply_output_state(node->sway_output, &instruction->output_state); |
304 | break; | 285 | break; |
305 | case N_WORKSPACE: | 286 | case N_WORKSPACE: |
306 | apply_workspace_state(node->sway_workspace, | 287 | apply_workspace_state(node->sway_workspace, |
307 | instruction->workspace_state); | 288 | &instruction->workspace_state); |
308 | break; | 289 | break; |
309 | case N_CONTAINER: | 290 | case N_CONTAINER: |
310 | apply_container_state(node->sway_container, | 291 | apply_container_state(node->sway_container, |
311 | instruction->container_state); | 292 | &instruction->container_state); |
312 | break; | 293 | break; |
313 | } | 294 | } |
314 | 295 | ||
@@ -334,7 +315,7 @@ static bool transaction_same_nodes(struct sway_transaction *a, | |||
334 | return true; | 315 | return true; |
335 | } | 316 | } |
336 | 317 | ||
337 | static void transaction_progress_queue() { | 318 | static void transaction_progress_queue(void) { |
338 | if (!server.transactions->length) { | 319 | if (!server.transactions->length) { |
339 | return; | 320 | return; |
340 | } | 321 | } |
@@ -389,7 +370,7 @@ static bool should_configure(struct sway_node *node, | |||
389 | return false; | 370 | return false; |
390 | } | 371 | } |
391 | struct sway_container_state *cstate = &node->sway_container->current; | 372 | struct sway_container_state *cstate = &node->sway_container->current; |
392 | struct sway_container_state *istate = instruction->container_state; | 373 | struct sway_container_state *istate = &instruction->container_state; |
393 | #ifdef HAVE_XWAYLAND | 374 | #ifdef HAVE_XWAYLAND |
394 | // Xwayland views are position-aware and need to be reconfigured | 375 | // Xwayland views are position-aware and need to be reconfigured |
395 | // when their position changes. | 376 | // when their position changes. |
@@ -417,10 +398,10 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
417 | struct sway_node *node = instruction->node; | 398 | struct sway_node *node = instruction->node; |
418 | if (should_configure(node, instruction)) { | 399 | if (should_configure(node, instruction)) { |
419 | instruction->serial = view_configure(node->sway_container->view, | 400 | instruction->serial = view_configure(node->sway_container->view, |
420 | instruction->container_state->view_x, | 401 | instruction->container_state.view_x, |
421 | instruction->container_state->view_y, | 402 | instruction->container_state.view_y, |
422 | instruction->container_state->view_width, | 403 | instruction->container_state.view_width, |
423 | instruction->container_state->view_height); | 404 | instruction->container_state.view_height); |
424 | ++transaction->num_waiting; | 405 | ++transaction->num_waiting; |
425 | 406 | ||
426 | // From here on we are rendering a saved buffer of the view, which | 407 | // From here on we are rendering a saved buffer of the view, which |
@@ -512,8 +493,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, | |||
512 | int width, int height) { | 493 | int width, int height) { |
513 | struct sway_transaction_instruction *instruction = | 494 | struct sway_transaction_instruction *instruction = |
514 | view->container->node.instruction; | 495 | view->container->node.instruction; |
515 | if (instruction->container_state->view_width == width && | 496 | if (instruction->container_state.view_width == width && |
516 | instruction->container_state->view_height == height) { | 497 | instruction->container_state.view_height == height) { |
517 | set_instruction_ready(instruction); | 498 | set_instruction_ready(instruction); |
518 | } | 499 | } |
519 | } | 500 | } |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index eab102fd..afad6f6f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -30,7 +30,7 @@ | |||
30 | // when dragging to the edge of a layout container. | 30 | // when dragging to the edge of a layout container. |
31 | #define DROP_LAYOUT_BORDER 30 | 31 | #define DROP_LAYOUT_BORDER 30 |
32 | 32 | ||
33 | static uint32_t get_current_time_msec() { | 33 | static uint32_t get_current_time_msec(void) { |
34 | struct timespec now; | 34 | struct timespec now; |
35 | clock_gettime(CLOCK_MONOTONIC, &now); | 35 | clock_gettime(CLOCK_MONOTONIC, &now); |
36 | return now.tv_nsec / 1000; | 36 | return now.tv_nsec / 1000; |
@@ -1007,8 +1007,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
1007 | if (seat_is_input_allowed(seat, surface)) { | 1007 | if (seat_is_input_allowed(seat, surface)) { |
1008 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, | 1008 | wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, |
1009 | event->touch_id, sx, sy); | 1009 | event->touch_id, sx, sy); |
1010 | cursor->image_client = NULL; | 1010 | cursor_set_image(cursor, NULL, NULL); |
1011 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | ||
1012 | } | 1011 | } |
1013 | } | 1012 | } |
1014 | 1013 | ||
@@ -1176,11 +1175,13 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
1176 | 1175 | ||
1177 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | 1176 | void cursor_set_image(struct sway_cursor *cursor, const char *image, |
1178 | struct wl_client *client) { | 1177 | struct wl_client *client) { |
1179 | if (!cursor->image || strcmp(cursor->image, image) != 0) { | 1178 | if (!image) { |
1179 | wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); | ||
1180 | } else if (!cursor->image || strcmp(cursor->image, image) != 0) { | ||
1180 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | 1181 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, |
1181 | cursor->cursor); | 1182 | cursor->cursor); |
1182 | cursor->image = image; | ||
1183 | } | 1183 | } |
1184 | cursor->image = image; | ||
1184 | cursor->image_client = client; | 1185 | cursor->image_client = client; |
1185 | } | 1186 | } |
1186 | 1187 | ||
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f39fe29c..32f0355e 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -120,6 +120,13 @@ static void input_manager_libinput_config_pointer( | |||
120 | libinput_device_config_click_set_method(libinput_device, | 120 | libinput_device_config_click_set_method(libinput_device, |
121 | ic->click_method); | 121 | ic->click_method); |
122 | } | 122 | } |
123 | if (ic->drag != INT_MIN) { | ||
124 | wlr_log(WLR_DEBUG, | ||
125 | "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", | ||
126 | ic->identifier, ic->click_method); | ||
127 | libinput_device_config_tap_set_drag_enabled(libinput_device, | ||
128 | ic->drag); | ||
129 | } | ||
123 | if (ic->drag_lock != INT_MIN) { | 130 | if (ic->drag_lock != INT_MIN) { |
124 | wlr_log(WLR_DEBUG, | 131 | wlr_log(WLR_DEBUG, |
125 | "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", | 132 | "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", |
diff --git a/sway/input/seat.c b/sway/input/seat.c index a9c564e7..e10b6409 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -144,32 +144,43 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { | |||
144 | struct sway_node *parent = node_get_parent(node); | 144 | struct sway_node *parent = node_get_parent(node); |
145 | struct sway_node *focus = seat_get_focus(seat); | 145 | struct sway_node *focus = seat_get_focus(seat); |
146 | 146 | ||
147 | bool set_focus = | 147 | if (node->type == N_WORKSPACE) { |
148 | focus != NULL && | 148 | seat_node_destroy(seat_node); |
149 | (focus == node || node_has_ancestor(focus, node)) && | 149 | return; |
150 | node->type == N_CONTAINER; | 150 | } |
151 | |||
152 | // Even though the container being destroyed might be nowhere near the | ||
153 | // focused container, we still need to set focus_inactive on a sibling of | ||
154 | // the container being destroyed. | ||
155 | bool needs_new_focus = focus && | ||
156 | (focus == node || node_has_ancestor(focus, node)); | ||
151 | 157 | ||
152 | seat_node_destroy(seat_node); | 158 | seat_node_destroy(seat_node); |
153 | 159 | ||
154 | if (set_focus) { | 160 | // Find new focus_inactive (ie. sibling, or workspace if no siblings left) |
155 | struct sway_node *next_focus = NULL; | 161 | struct sway_node *next_focus = NULL; |
156 | while (next_focus == NULL) { | 162 | while (next_focus == NULL) { |
157 | struct sway_container *con = | 163 | struct sway_container *con = |
158 | seat_get_focus_inactive_view(seat, parent); | 164 | seat_get_focus_inactive_view(seat, parent); |
159 | next_focus = con ? &con->node : NULL; | 165 | next_focus = con ? &con->node : NULL; |
160 | 166 | ||
161 | if (next_focus == NULL && parent->type == N_WORKSPACE) { | 167 | if (next_focus == NULL && parent->type == N_WORKSPACE) { |
162 | next_focus = parent; | 168 | next_focus = parent; |
163 | break; | 169 | break; |
164 | } | ||
165 | |||
166 | parent = node_get_parent(parent); | ||
167 | } | 170 | } |
168 | 171 | ||
169 | // the structure change might have caused it to move up to the top of | 172 | parent = node_get_parent(parent); |
173 | } | ||
174 | |||
175 | if (needs_new_focus) { | ||
176 | // The structure change might have caused it to move up to the top of | ||
170 | // the focus stack without sending focus notifications to the view | 177 | // the focus stack without sending focus notifications to the view |
171 | seat_send_focus(next_focus, seat); | 178 | seat_send_focus(next_focus, seat); |
172 | seat_set_focus(seat, next_focus); | 179 | seat_set_focus(seat, next_focus); |
180 | } else { | ||
181 | // Setting focus_inactive | ||
182 | seat_set_focus_warp(seat, next_focus, false, false); | ||
183 | seat_set_focus_warp(seat, focus, false, false); | ||
173 | } | 184 | } |
174 | } | 185 | } |
175 | 186 | ||
@@ -368,11 +379,20 @@ static void seat_update_capabilities(struct sway_seat *seat) { | |||
368 | caps |= WL_SEAT_CAPABILITY_TOUCH; | 379 | caps |= WL_SEAT_CAPABILITY_TOUCH; |
369 | break; | 380 | break; |
370 | case WLR_INPUT_DEVICE_TABLET_TOOL: | 381 | case WLR_INPUT_DEVICE_TABLET_TOOL: |
382 | caps |= WL_SEAT_CAPABILITY_POINTER; | ||
383 | break; | ||
371 | case WLR_INPUT_DEVICE_TABLET_PAD: | 384 | case WLR_INPUT_DEVICE_TABLET_PAD: |
372 | break; | 385 | break; |
373 | } | 386 | } |
374 | } | 387 | } |
375 | wlr_seat_set_capabilities(seat->wlr_seat, caps); | 388 | wlr_seat_set_capabilities(seat->wlr_seat, caps); |
389 | |||
390 | // Hide cursor if seat doesn't have pointer capability | ||
391 | if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) { | ||
392 | cursor_set_image(seat->cursor, NULL, NULL); | ||
393 | } else { | ||
394 | cursor_set_image(seat->cursor, "left_ptr", NULL); | ||
395 | } | ||
376 | } | 396 | } |
377 | 397 | ||
378 | static void seat_apply_input_config(struct sway_seat *seat, | 398 | static void seat_apply_input_config(struct sway_seat *seat, |
@@ -552,8 +572,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
552 | output->name, (double)output->scale); | 572 | output->name, (double)output->scale); |
553 | } | 573 | } |
554 | 574 | ||
555 | wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager, | 575 | cursor_set_image(seat->cursor, "left_ptr", NULL); |
556 | "left_ptr", seat->cursor->cursor); | ||
557 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, | 576 | wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, |
558 | seat->cursor->cursor->y); | 577 | seat->cursor->cursor->y); |
559 | } | 578 | } |
@@ -752,6 +771,12 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, | |||
752 | 771 | ||
753 | seat->has_focus = true; | 772 | seat->has_focus = true; |
754 | 773 | ||
774 | if (config->smart_gaps) { | ||
775 | // When smart gaps is on, gaps may change when the focus changes so | ||
776 | // the workspace needs to be arranged | ||
777 | arrange_workspace(new_workspace); | ||
778 | } | ||
779 | |||
755 | update_debug_tree(); | 780 | update_debug_tree(); |
756 | } | 781 | } |
757 | 782 | ||
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index df24b812..bc36f9b1 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -42,7 +42,7 @@ static const char *ipc_json_orientation_description(enum sway_container_layout l | |||
42 | return "none"; | 42 | return "none"; |
43 | } | 43 | } |
44 | 44 | ||
45 | json_object *ipc_json_get_version() { | 45 | json_object *ipc_json_get_version(void) { |
46 | int major = 0, minor = 0, patch = 0; | 46 | int major = 0, minor = 0, patch = 0; |
47 | json_object *version = json_object_new_object(); | 47 | json_object *version = json_object_new_object(); |
48 | 48 | ||
diff --git a/sway/main.c b/sway/main.c index 990f5f3a..dea4a31c 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -12,10 +12,6 @@ | |||
12 | #include <sys/wait.h> | 12 | #include <sys/wait.h> |
13 | #include <sys/un.h> | 13 | #include <sys/un.h> |
14 | #include <unistd.h> | 14 | #include <unistd.h> |
15 | #ifdef __linux__ | ||
16 | #include <sys/capability.h> | ||
17 | #include <sys/prctl.h> | ||
18 | #endif | ||
19 | #include <wlr/util/log.h> | 15 | #include <wlr/util/log.h> |
20 | #include "sway/commands.h" | 16 | #include "sway/commands.h" |
21 | #include "sway/config.h" | 17 | #include "sway/config.h" |
@@ -45,7 +41,7 @@ void sig_handler(int signal) { | |||
45 | sway_terminate(EXIT_SUCCESS); | 41 | sway_terminate(EXIT_SUCCESS); |
46 | } | 42 | } |
47 | 43 | ||
48 | void detect_raspi() { | 44 | void detect_raspi(void) { |
49 | bool raspi = false; | 45 | bool raspi = false; |
50 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); | 46 | FILE *f = fopen("/sys/firmware/devicetree/base/model", "r"); |
51 | if (!f) { | 47 | if (!f) { |
@@ -85,7 +81,7 @@ void detect_raspi() { | |||
85 | } | 81 | } |
86 | } | 82 | } |
87 | 83 | ||
88 | void detect_proprietary() { | 84 | void detect_proprietary(void) { |
89 | FILE *f = fopen("/proc/modules", "r"); | 85 | FILE *f = fopen("/proc/modules", "r"); |
90 | if (!f) { | 86 | if (!f) { |
91 | return; | 87 | return; |
@@ -120,7 +116,7 @@ void run_as_ipc_client(char *command, char *socket_path) { | |||
120 | close(socketfd); | 116 | close(socketfd); |
121 | } | 117 | } |
122 | 118 | ||
123 | static void log_env() { | 119 | static void log_env(void) { |
124 | const char *log_vars[] = { | 120 | const char *log_vars[] = { |
125 | "PATH", | 121 | "PATH", |
126 | "LD_LIBRARY_PATH", | 122 | "LD_LIBRARY_PATH", |
@@ -135,7 +131,7 @@ static void log_env() { | |||
135 | } | 131 | } |
136 | } | 132 | } |
137 | 133 | ||
138 | static void log_distro() { | 134 | static void log_distro(void) { |
139 | const char *paths[] = { | 135 | const char *paths[] = { |
140 | "/etc/lsb-release", | 136 | "/etc/lsb-release", |
141 | "/etc/os-release", | 137 | "/etc/os-release", |
@@ -162,7 +158,7 @@ static void log_distro() { | |||
162 | } | 158 | } |
163 | } | 159 | } |
164 | 160 | ||
165 | static void log_kernel() { | 161 | static void log_kernel(void) { |
166 | FILE *f = popen("uname -a", "r"); | 162 | FILE *f = popen("uname -a", "r"); |
167 | if (!f) { | 163 | if (!f) { |
168 | wlr_log(WLR_INFO, "Unable to determine kernel version"); | 164 | wlr_log(WLR_INFO, "Unable to determine kernel version"); |
@@ -181,28 +177,8 @@ static void log_kernel() { | |||
181 | pclose(f); | 177 | pclose(f); |
182 | } | 178 | } |
183 | 179 | ||
184 | static void executable_sanity_check() { | ||
185 | #ifdef __linux__ | ||
186 | struct stat sb; | ||
187 | char *exe = realpath("/proc/self/exe", NULL); | ||
188 | stat(exe, &sb); | ||
189 | // We assume that cap_get_file returning NULL implies ENODATA | ||
190 | if (sb.st_mode & (S_ISUID|S_ISGID) && cap_get_file(exe)) { | ||
191 | wlr_log(WLR_ERROR, | ||
192 | "sway executable has both the s(g)uid bit AND file caps set."); | ||
193 | wlr_log(WLR_ERROR, | ||
194 | "This is strongly discouraged (and completely broken)."); | ||
195 | wlr_log(WLR_ERROR, | ||
196 | "Please clear one of them (either the suid bit, or the file caps)."); | ||
197 | wlr_log(WLR_ERROR, | ||
198 | "If unsure, strip the file caps."); | ||
199 | exit(EXIT_FAILURE); | ||
200 | } | ||
201 | free(exe); | ||
202 | #endif | ||
203 | } | ||
204 | 180 | ||
205 | static void drop_permissions(bool keep_caps) { | 181 | static void drop_permissions(void) { |
206 | if (getuid() != geteuid() || getgid() != getegid()) { | 182 | if (getuid() != geteuid() || getgid() != getegid()) { |
207 | if (setgid(getgid()) != 0) { | 183 | if (setgid(getgid()) != 0) { |
208 | wlr_log(WLR_ERROR, "Unable to drop root"); | 184 | wlr_log(WLR_ERROR, "Unable to drop root"); |
@@ -217,20 +193,6 @@ static void drop_permissions(bool keep_caps) { | |||
217 | wlr_log(WLR_ERROR, "Root privileges can be restored."); | 193 | wlr_log(WLR_ERROR, "Root privileges can be restored."); |
218 | exit(EXIT_FAILURE); | 194 | exit(EXIT_FAILURE); |
219 | } | 195 | } |
220 | #ifdef __linux__ | ||
221 | if (keep_caps) { | ||
222 | // Drop every cap except CAP_SYS_PTRACE | ||
223 | cap_t caps = cap_init(); | ||
224 | cap_value_t keep = CAP_SYS_PTRACE; | ||
225 | wlr_log(WLR_INFO, "Dropping extra capabilities"); | ||
226 | if (cap_set_flag(caps, CAP_PERMITTED, 1, &keep, CAP_SET) || | ||
227 | cap_set_flag(caps, CAP_EFFECTIVE, 1, &keep, CAP_SET) || | ||
228 | cap_set_proc(caps)) { | ||
229 | wlr_log(WLR_ERROR, "Failed to drop extra capabilities"); | ||
230 | exit(EXIT_FAILURE); | ||
231 | } | ||
232 | } | ||
233 | #endif | ||
234 | } | 196 | } |
235 | 197 | ||
236 | void enable_debug_flag(const char *flag) { | 198 | void enable_debug_flag(const char *flag) { |
@@ -279,14 +241,6 @@ int main(int argc, char **argv) { | |||
279 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" | 241 | " --get-socketpath Gets the IPC socket path and prints it, then exits.\n" |
280 | "\n"; | 242 | "\n"; |
281 | 243 | ||
282 | // Security: | ||
283 | unsetenv("LD_PRELOAD"); | ||
284 | #ifdef _LD_LIBRARY_PATH | ||
285 | setenv("LD_LIBRARY_PATH", _LD_LIBRARY_PATH, 1); | ||
286 | #else | ||
287 | unsetenv("LD_LIBRARY_PATH"); | ||
288 | #endif | ||
289 | |||
290 | int c; | 244 | int c; |
291 | while (1) { | 245 | while (1) { |
292 | int option_index = 0; | 246 | int option_index = 0; |
@@ -347,7 +301,7 @@ int main(int argc, char **argv) { | |||
347 | wlr_log(WLR_ERROR, "Don't use options with the IPC client"); | 301 | wlr_log(WLR_ERROR, "Don't use options with the IPC client"); |
348 | exit(EXIT_FAILURE); | 302 | exit(EXIT_FAILURE); |
349 | } | 303 | } |
350 | drop_permissions(false); | 304 | drop_permissions(); |
351 | char *socket_path = getenv("SWAYSOCK"); | 305 | char *socket_path = getenv("SWAYSOCK"); |
352 | if (!socket_path) { | 306 | if (!socket_path) { |
353 | wlr_log(WLR_ERROR, "Unable to retrieve socket path"); | 307 | wlr_log(WLR_ERROR, "Unable to retrieve socket path"); |
@@ -358,34 +312,17 @@ int main(int argc, char **argv) { | |||
358 | return 0; | 312 | return 0; |
359 | } | 313 | } |
360 | 314 | ||
361 | executable_sanity_check(); | ||
362 | bool suid = false; | ||
363 | |||
364 | if (!server_privileged_prepare(&server)) { | 315 | if (!server_privileged_prepare(&server)) { |
365 | return 1; | 316 | return 1; |
366 | } | 317 | } |
367 | 318 | ||
368 | #if defined(__linux__) || defined(__FreeBSD__) | ||
369 | if (getuid() != geteuid() || getgid() != getegid()) { | ||
370 | #ifdef __linux__ | ||
371 | // Retain capabilities after setuid() | ||
372 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { | ||
373 | wlr_log(WLR_ERROR, "Cannot keep caps after setuid()"); | ||
374 | exit(EXIT_FAILURE); | ||
375 | } | ||
376 | #endif | ||
377 | suid = true; | ||
378 | } | ||
379 | #endif | ||
380 | |||
381 | log_kernel(); | 319 | log_kernel(); |
382 | log_distro(); | 320 | log_distro(); |
383 | detect_proprietary(); | 321 | detect_proprietary(); |
384 | detect_raspi(); | 322 | detect_raspi(); |
385 | 323 | ||
386 | #if defined(__linux__) || defined(__FreeBSD__) | 324 | drop_permissions(); |
387 | drop_permissions(suid); | 325 | |
388 | #endif | ||
389 | // handle SIGTERM signals | 326 | // handle SIGTERM signals |
390 | signal(SIGTERM, sig_handler); | 327 | signal(SIGTERM, sig_handler); |
391 | 328 | ||
diff --git a/sway/meson.build b/sway/meson.build index 2a3c5b5e..00ebcb40 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -79,6 +79,7 @@ sway_sources = files( | |||
79 | 'commands/seat/fallback.c', | 79 | 'commands/seat/fallback.c', |
80 | 'commands/set.c', | 80 | 'commands/set.c', |
81 | 'commands/show_marks.c', | 81 | 'commands/show_marks.c', |
82 | 'commands/smart_borders.c', | ||
82 | 'commands/smart_gaps.c', | 83 | 'commands/smart_gaps.c', |
83 | 'commands/split.c', | 84 | 'commands/split.c', |
84 | 'commands/sticky.c', | 85 | 'commands/sticky.c', |
@@ -120,6 +121,7 @@ sway_sources = files( | |||
120 | 121 | ||
121 | 'commands/input/accel_profile.c', | 122 | 'commands/input/accel_profile.c', |
122 | 'commands/input/click_method.c', | 123 | 'commands/input/click_method.c', |
124 | 'commands/input/drag.c', | ||
123 | 'commands/input/drag_lock.c', | 125 | 'commands/input/drag_lock.c', |
124 | 'commands/input/dwt.c', | 126 | 'commands/input/dwt.c', |
125 | 'commands/input/events.c', | 127 | 'commands/input/events.c', |
@@ -165,7 +167,6 @@ sway_deps = [ | |||
165 | cairo, | 167 | cairo, |
166 | gdk_pixbuf, | 168 | gdk_pixbuf, |
167 | jsonc, | 169 | jsonc, |
168 | libcap, | ||
169 | libinput, | 170 | libinput, |
170 | math, | 171 | math, |
171 | pango, | 172 | pango, |
@@ -188,5 +189,6 @@ executable( | |||
188 | include_directories: [sway_inc], | 189 | include_directories: [sway_inc], |
189 | dependencies: sway_deps, | 190 | dependencies: sway_deps, |
190 | link_with: [lib_sway_common], | 191 | link_with: [lib_sway_common], |
192 | install_rpath : rpathdir, | ||
191 | install: true | 193 | install: true |
192 | ) | 194 | ) |
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index a61e2829..00b9386e 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd | |||
@@ -6,8 +6,7 @@ sway-bar - bar configuration file and commands | |||
6 | 6 | ||
7 | # DESCRIPTION | 7 | # DESCRIPTION |
8 | 8 | ||
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..14f2a007 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -7,7 +7,6 @@ sway-input - input configuration file and commands | |||
7 | # DESCRIPTION | 7 | # DESCRIPTION |
8 | 8 | ||
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 |
@@ -68,6 +67,9 @@ The following commands may only be used in the configuration file. | |||
68 | *input* <identifier> click\_method none|button\_areas|clickfinger | 67 | *input* <identifier> click\_method none|button\_areas|clickfinger |
69 | Changes the click method for the specified device. | 68 | Changes the click method for the specified device. |
70 | 69 | ||
70 | *input* <identifier> drag enabled|disabled | ||
71 | Enables or disables tap-and-drag for specified input device. | ||
72 | |||
71 | *input* <identifier> drag\_lock enabled|disabled | 73 | *input* <identifier> drag\_lock enabled|disabled |
72 | Enables or disables drag lock for specified input device. | 74 | Enables or disables drag lock for specified input device. |
73 | 75 | ||
@@ -116,8 +118,7 @@ The following commands may only be used in the configuration file. | |||
116 | 118 | ||
117 | ## SEAT CONFIGURATION | 119 | ## SEAT CONFIGURATION |
118 | 120 | ||
119 | Configure options for multiseat mode. sway-seat commands must be used inside a | 121 | Configure options for multiseat mode. |
120 | _seat { }_ block in the config. | ||
121 | 122 | ||
122 | A *seat* is a collection of input devices that act independently of each other. | 123 | 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 | 124 | 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..3202d517 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -19,6 +19,24 @@ bindsym Shift+XF86AudioRaiseVolume exec \\ | |||
19 | pactl set-sink-volume @DEFAULT_SINK@ -1% | 19 | pactl set-sink-volume @DEFAULT_SINK@ -1% |
20 | ``` | 20 | ``` |
21 | 21 | ||
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. |
@@ -407,37 +419,30 @@ The default colors are: | |||
407 | inner gap is nonzero. When _off_, gaps will only be added between views. | 419 | inner gap is nonzero. When _off_, gaps will only be added between views. |
408 | _toggle_ cannot be used in the configuration file. | 420 | _toggle_ cannot be used in the configuration file. |
409 | 421 | ||
410 | *gaps* <amount> | ||
411 | Sets _amount_ pixels of gap between windows and around each workspace. | ||
412 | |||
413 | *gaps* inner|outer <amount> | 422 | *gaps* inner|outer <amount> |
414 | Sets default _amount_ pixels of _inner_ or _outer_ gap, where the former | 423 | Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner |
415 | affects spacing between views and the latter affects the space around each | 424 | affects spacing around each view and outer affects the spacing around each |
416 | workspace. | 425 | workspace. Outer gaps are in addition to inner gaps. |
417 | 426 | ||
418 | *gaps* inner|outer all|workspace|current set|plus|minus <amount> | 427 | This affects new workspaces only, and is used when the workspace doesn't |
419 | Changes the gaps for the _inner_ or _outer_ gap. _all_ changes the gaps for | 428 | have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). |
420 | all views or workspace, _workspace_ changes gaps for all views in current | ||
421 | workspace (or current workspace), and _current_ changes gaps for the current | ||
422 | view or workspace. | ||
423 | 429 | ||
424 | *hide\_edge\_borders* none|vertical|horizontal|both|smart | 430 | *gaps* inner|outer all|current set|plus|minus <amount> |
431 | Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the | ||
432 | _current_ workspace. | ||
433 | |||
434 | *hide\_edge\_borders* none|vertical|horizontal|both|smart|smart\_no\_gaps | ||
425 | Hides window borders adjacent to the screen edges. Default is _none_. | 435 | Hides window borders adjacent to the screen edges. Default is _none_. |
426 | 436 | ||
427 | *input* <input\_device> *{* <commands...> *}* | 437 | *input* <input\_device> <input-subcommands...> |
428 | _commands..._ after *{* will be interpreted as input commands applying to | 438 | For details on input subcommands, see *sway-input*(5). |
429 | the specified input device. For details, see *sway-input*(5). A newline is | ||
430 | required between *{* and the first command, and *}* must be alone on a | ||
431 | line. | ||
432 | 439 | ||
433 | \* may be used in lieu of a specific device name to configure all input | 440 | \* may be used in lieu of a specific device name to configure all input |
434 | devices. A list of input device names may be obtained via *swaymsg -t | 441 | devices. A list of input device names may be obtained via *swaymsg -t |
435 | get\_inputs*. | 442 | get\_inputs*. |
436 | 443 | ||
437 | *seat* <seat> *{* <commands...> *}* | 444 | *seat* <seat> <seat-subcommands...> |
438 | _commands..._ after *{* will be interpreted as seat commands applying to | 445 | For details on seat subcommands, see *sway-input*(5). |
439 | the specified seat. For details, see *sway-input*(5). A newline is required | ||
440 | between *{* and the first command, and *}* must be alone on a line. | ||
441 | 446 | ||
442 | *seat* <seat> cursor move|set <x> <y> | 447 | *seat* <seat> cursor move|set <x> <y> |
443 | Move specified seat's cursor relative to current position or wrap to | 448 | Move specified seat's cursor relative to current position or wrap to |
@@ -451,6 +456,12 @@ The default colors are: | |||
451 | *kill* | 456 | *kill* |
452 | Kills (closes) the currently focused container and all of its children. | 457 | Kills (closes) the currently focused container and all of its children. |
453 | 458 | ||
459 | *smart\_borders* on|no\_gaps|off | ||
460 | If smart\_borders are _on_, borders will only be enabled if the workspace | ||
461 | only has one visible child (identical to _hide\_edge\_borders_ smart). If | ||
462 | smart\_borders is set to _no\_gaps_, borders will only be enabled if the | ||
463 | workspace only has one visible child and gaps greater than zero. | ||
464 | |||
454 | *smart\_gaps* on|off | 465 | *smart\_gaps* on|off |
455 | If smart\_gaps are _on_ gaps will only be enabled if a workspace has more | 466 | If smart\_gaps are _on_ gaps will only be enabled if a workspace has more |
456 | than one child. | 467 | than one child. |
@@ -465,10 +476,8 @@ The default colors are: | |||
465 | *mode* <mode> | 476 | *mode* <mode> |
466 | Switches to the specified mode. The default mode _default_. | 477 | Switches to the specified mode. The default mode _default_. |
467 | 478 | ||
468 | *mode* [--pango\_markup] <mode> *{* <commands...> *}* | 479 | *mode* [--pango\_markup] <mode> <mode-subcommands...> |
469 | _commands..._ after *{* will be added to the specified mode. A newline is | 480 | The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*. |
470 | required between *{* and the first command, and *}* must be alone on a | ||
471 | line. Only *bindsym* and *bindcode* commands are permitted in mode blocks. | ||
472 | If _--pango\_markup_ is given, then _mode_ will be interpreted as pango | 481 | If _--pango\_markup_ is given, then _mode_ will be interpreted as pango |
473 | markup. | 482 | markup. |
474 | 483 | ||
@@ -533,8 +542,15 @@ You may combine output commands into one, like so: | |||
533 | output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch | 542 | output HDMI-A-1 mode 1920x1080 pos 1920,0 bg ~/wallpaper.png stretch |
534 | 543 | ||
535 | You can get a list of output names with *swaymsg -t get\_outputs*. You may also | 544 | 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 | 545 | match any output by using the output name "\*". |
537 | config after the others, or it will be matched instead of the others. | 546 | |
547 | *set* $<name> <value> | ||
548 | Sets variable $_name_ to _value_. You can use the new variable in the | ||
549 | arguments of future commands. When the variable is used, it can be escaped | ||
550 | with an additional $ (ie $$_name_) to have the replacement happen at run | ||
551 | time instead of when reading the config. However, it does not always make | ||
552 | sense for the variable to be replaced at run time since some arguments do | ||
553 | need to be known at config time. | ||
538 | 554 | ||
539 | *show\_marks* yes|no | 555 | *show\_marks* yes|no |
540 | If *show\_marks* is yes, marks will be displayed in the window borders. | 556 | If *show\_marks* is yes, marks will be displayed in the window borders. |
@@ -568,6 +584,10 @@ config after the others, or it will be matched instead of the others. | |||
568 | *workspace* back_and_forth | 584 | *workspace* back_and_forth |
569 | Switches to the previously focused workspace. | 585 | Switches to the previously focused workspace. |
570 | 586 | ||
587 | *workspace* <name> gaps inner|outer <amount> | ||
588 | Specifies that workspace _name_ should have the given gaps settings when it | ||
589 | is created. | ||
590 | |||
571 | *workspace* <name> output <output> | 591 | *workspace* <name> output <output> |
572 | Specifies that workspace _name_ should be shown on the specified _output_. | 592 | Specifies that workspace _name_ should be shown on the specified _output_. |
573 | 593 | ||
diff --git a/sway/tree/container.c b/sway/tree/container.c index 9b671c1d..a069b177 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -593,7 +593,7 @@ void container_update_representation(struct sway_container *con) { | |||
593 | } | 593 | } |
594 | } | 594 | } |
595 | 595 | ||
596 | size_t container_titlebar_height() { | 596 | size_t container_titlebar_height(void) { |
597 | return config->font_height + TITLEBAR_V_PADDING * 2; | 597 | return config->font_height + TITLEBAR_V_PADDING * 2; |
598 | } | 598 | } |
599 | 599 | ||
@@ -829,9 +829,16 @@ void container_floating_move_to_center(struct sway_container *con) { | |||
829 | return; | 829 | return; |
830 | } | 830 | } |
831 | struct sway_workspace *ws = con->workspace; | 831 | struct sway_workspace *ws = con->workspace; |
832 | bool full = con->is_fullscreen; | ||
833 | if (full) { | ||
834 | container_set_fullscreen(con, false); | ||
835 | } | ||
832 | double new_lx = ws->x + (ws->width - con->width) / 2; | 836 | double new_lx = ws->x + (ws->width - con->width) / 2; |
833 | double new_ly = ws->y + (ws->height - con->height) / 2; | 837 | double new_ly = ws->y + (ws->height - con->height) / 2; |
834 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); | 838 | container_floating_translate(con, new_lx - con->x, new_ly - con->y); |
839 | if (full) { | ||
840 | container_set_fullscreen(con, true); | ||
841 | } | ||
835 | } | 842 | } |
836 | 843 | ||
837 | static bool find_urgent_iterator(struct sway_container *con, void *data) { | 844 | static bool find_urgent_iterator(struct sway_container *con, void *data) { |
@@ -1020,15 +1027,33 @@ void container_add_gaps(struct sway_container *c) { | |||
1020 | if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { | 1027 | if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { |
1021 | return; | 1028 | return; |
1022 | } | 1029 | } |
1023 | // Children of tabbed/stacked containers re-use the gaps of the container | 1030 | // Descendants of tabbed/stacked containers re-use the gaps of the container |
1024 | enum sway_container_layout layout = container_parent_layout(c); | 1031 | struct sway_container *temp = c; |
1025 | if (layout == L_TABBED || layout == L_STACKED) { | 1032 | while (temp) { |
1026 | return; | 1033 | enum sway_container_layout layout = container_parent_layout(temp); |
1034 | if (layout == L_TABBED || layout == L_STACKED) { | ||
1035 | return; | ||
1036 | } | ||
1037 | temp = temp->parent; | ||
1038 | } | ||
1039 | // If smart gaps is on, don't add gaps if there is only one view visible | ||
1040 | if (config->smart_gaps) { | ||
1041 | struct sway_view *view = c->view; | ||
1042 | if (!view) { | ||
1043 | struct sway_seat *seat = | ||
1044 | input_manager_get_default_seat(input_manager); | ||
1045 | struct sway_container *focus = | ||
1046 | seat_get_focus_inactive_view(seat, &c->node); | ||
1047 | view = focus ? focus->view : NULL; | ||
1048 | } | ||
1049 | if (view && view_is_only_visible(view)) { | ||
1050 | return; | ||
1051 | } | ||
1027 | } | 1052 | } |
1028 | 1053 | ||
1029 | struct sway_workspace *ws = c->workspace; | 1054 | struct sway_workspace *ws = c->workspace; |
1030 | 1055 | ||
1031 | c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; | 1056 | c->current_gaps = ws->gaps_inner; |
1032 | c->x += c->current_gaps; | 1057 | c->x += c->current_gaps; |
1033 | c->y += c->current_gaps; | 1058 | c->y += c->current_gaps; |
1034 | c->width -= 2 * c->current_gaps; | 1059 | c->width -= 2 * c->current_gaps; |
diff --git a/sway/tree/output.c b/sway/tree/output.c index 06933dc4..c3176325 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -113,6 +113,20 @@ void output_enable(struct sway_output *output, struct output_config *oc) { | |||
113 | arrange_root(); | 113 | arrange_root(); |
114 | } | 114 | } |
115 | 115 | ||
116 | static void evacuate_sticky(struct sway_workspace *old_ws, | ||
117 | struct sway_output *new_output) { | ||
118 | struct sway_workspace *new_ws = output_get_active_workspace(new_output); | ||
119 | while (old_ws->floating->length) { | ||
120 | struct sway_container *sticky = old_ws->floating->items[0]; | ||
121 | container_detach(sticky); | ||
122 | workspace_add_floating(new_ws, sticky); | ||
123 | container_handle_fullscreen_reparent(sticky); | ||
124 | container_floating_move_to_center(sticky); | ||
125 | ipc_event_window(sticky, "move"); | ||
126 | } | ||
127 | workspace_detect_urgent(new_ws); | ||
128 | } | ||
129 | |||
116 | static void output_evacuate(struct sway_output *output) { | 130 | static void output_evacuate(struct sway_output *output) { |
117 | if (!output->workspaces->length) { | 131 | if (!output->workspaces->length) { |
118 | return; | 132 | return; |
@@ -130,17 +144,21 @@ static void output_evacuate(struct sway_output *output) { | |||
130 | 144 | ||
131 | workspace_detach(workspace); | 145 | workspace_detach(workspace); |
132 | 146 | ||
133 | if (workspace_is_empty(workspace)) { | ||
134 | workspace_begin_destroy(workspace); | ||
135 | continue; | ||
136 | } | ||
137 | |||
138 | struct sway_output *new_output = | 147 | struct sway_output *new_output = |
139 | workspace_output_get_highest_available(workspace, output); | 148 | workspace_output_get_highest_available(workspace, output); |
140 | if (!new_output) { | 149 | if (!new_output) { |
141 | new_output = fallback_output; | 150 | new_output = fallback_output; |
142 | } | 151 | } |
143 | 152 | ||
153 | if (workspace_is_empty(workspace)) { | ||
154 | // If floating is not empty, there are sticky containers to move | ||
155 | if (workspace->floating->length) { | ||
156 | evacuate_sticky(workspace, new_output); | ||
157 | } | ||
158 | workspace_begin_destroy(workspace); | ||
159 | continue; | ||
160 | } | ||
161 | |||
144 | if (new_output) { | 162 | if (new_output) { |
145 | workspace_output_add_priority(workspace, new_output); | 163 | workspace_output_add_priority(workspace, new_output); |
146 | output_add_workspace(new_output, workspace); | 164 | output_add_workspace(new_output, workspace); |
diff --git a/sway/tree/root.c b/sway/tree/root.c index d6f67bd7..6748e9c9 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -273,6 +273,12 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), | |||
273 | container_for_each_child(container, f, data); | 273 | container_for_each_child(container, f, data); |
274 | } | 274 | } |
275 | } | 275 | } |
276 | |||
277 | // Saved workspaces | ||
278 | for (int i = 0; i < root->saved_workspaces->length; ++i) { | ||
279 | struct sway_workspace *ws = root->saved_workspaces->items[i]; | ||
280 | workspace_for_each_container(ws, f, data); | ||
281 | } | ||
276 | } | 282 | } |
277 | 283 | ||
278 | struct sway_output *root_find_output( | 284 | struct sway_output *root_find_output( |
@@ -320,6 +326,15 @@ struct sway_container *root_find_container( | |||
320 | } | 326 | } |
321 | } | 327 | } |
322 | } | 328 | } |
329 | |||
330 | // Saved workspaces | ||
331 | for (int i = 0; i < root->saved_workspaces->length; ++i) { | ||
332 | struct sway_workspace *ws = root->saved_workspaces->items[i]; | ||
333 | if ((result = workspace_find_container(ws, test, data))) { | ||
334 | return result; | ||
335 | } | ||
336 | } | ||
337 | |||
323 | return NULL; | 338 | return NULL; |
324 | } | 339 | } |
325 | 340 | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index 9ffcf206..9c7c44e9 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -165,6 +165,34 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
165 | return 0; | 165 | return 0; |
166 | } | 166 | } |
167 | 167 | ||
168 | bool view_is_only_visible(struct sway_view *view) { | ||
169 | bool only_view = true; | ||
170 | struct sway_container *con = view->container; | ||
171 | while (con) { | ||
172 | enum sway_container_layout layout = container_parent_layout(con); | ||
173 | if (layout != L_TABBED && layout != L_STACKED) { | ||
174 | list_t *siblings = container_get_siblings(con); | ||
175 | if (siblings && siblings->length > 1) { | ||
176 | only_view = false; | ||
177 | break; | ||
178 | } | ||
179 | } | ||
180 | con = con->parent; | ||
181 | } | ||
182 | return only_view; | ||
183 | } | ||
184 | |||
185 | static bool gaps_to_edge(struct sway_view *view) { | ||
186 | struct sway_container *con = view->container; | ||
187 | while (con) { | ||
188 | if (con->current_gaps > 0) { | ||
189 | return true; | ||
190 | } | ||
191 | con = con->parent; | ||
192 | } | ||
193 | return view->container->workspace->current_gaps > 0; | ||
194 | } | ||
195 | |||
168 | void view_autoconfigure(struct sway_view *view) { | 196 | void view_autoconfigure(struct sway_view *view) { |
169 | if (!view->container->workspace) { | 197 | if (!view->container->workspace) { |
170 | // Hidden in the scratchpad | 198 | // Hidden in the scratchpad |
@@ -181,39 +209,28 @@ void view_autoconfigure(struct sway_view *view) { | |||
181 | } | 209 | } |
182 | 210 | ||
183 | struct sway_workspace *ws = view->container->workspace; | 211 | struct sway_workspace *ws = view->container->workspace; |
184 | |||
185 | bool other_views = false; | ||
186 | if (config->hide_edge_borders == E_SMART) { | ||
187 | struct sway_container *con = view->container; | ||
188 | while (con) { | ||
189 | enum sway_container_layout layout = container_parent_layout(con); | ||
190 | if (layout != L_TABBED && layout != L_STACKED) { | ||
191 | list_t *siblings = container_get_siblings(con); | ||
192 | if (siblings && siblings->length > 1) { | ||
193 | other_views = true; | ||
194 | break; | ||
195 | } | ||
196 | } | ||
197 | con = con->parent; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | struct sway_container *con = view->container; | 212 | struct sway_container *con = view->container; |
202 | 213 | ||
214 | bool smart = config->hide_edge_borders == E_SMART || | ||
215 | config->hide_edge_borders == E_SMART_NO_GAPS; | ||
216 | bool other_views = smart && !view_is_only_visible(view); | ||
217 | bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS | ||
218 | || !gaps_to_edge(view); | ||
219 | |||
203 | view->border_top = view->border_bottom = true; | 220 | view->border_top = view->border_bottom = true; |
204 | view->border_left = view->border_right = true; | 221 | view->border_left = view->border_right = true; |
205 | if (config->hide_edge_borders == E_BOTH | 222 | if (config->hide_edge_borders == E_BOTH |
206 | || config->hide_edge_borders == E_VERTICAL | 223 | || config->hide_edge_borders == E_VERTICAL |
207 | || (config->hide_edge_borders == E_SMART && !other_views)) { | 224 | || (smart && !other_views && no_gaps)) { |
208 | view->border_left = con->x != ws->x; | 225 | view->border_left = con->x - con->current_gaps != ws->x; |
209 | int right_x = con->x + con->width; | 226 | int right_x = con->x + con->width + con->current_gaps; |
210 | view->border_right = right_x != ws->x + ws->width; | 227 | view->border_right = right_x != ws->x + ws->width; |
211 | } | 228 | } |
212 | if (config->hide_edge_borders == E_BOTH | 229 | if (config->hide_edge_borders == E_BOTH |
213 | || config->hide_edge_borders == E_HORIZONTAL | 230 | || config->hide_edge_borders == E_HORIZONTAL |
214 | || (config->hide_edge_borders == E_SMART && !other_views)) { | 231 | || (smart && !other_views && no_gaps)) { |
215 | view->border_top = con->y != ws->y; | 232 | view->border_top = con->y - con->current_gaps != ws->y; |
216 | int bottom_y = con->y + con->height; | 233 | int bottom_y = con->y + con->height + con->current_gaps; |
217 | view->border_bottom = bottom_y != ws->y + ws->height; | 234 | view->border_bottom = bottom_y != ws->y + ws->height; |
218 | } | 235 | } |
219 | 236 | ||
@@ -1004,12 +1021,16 @@ bool view_is_visible(struct sway_view *view) { | |||
1004 | floater = floater->parent; | 1021 | floater = floater->parent; |
1005 | } | 1022 | } |
1006 | bool is_sticky = container_is_floating(floater) && floater->is_sticky; | 1023 | bool is_sticky = container_is_floating(floater) && floater->is_sticky; |
1024 | if (!is_sticky && !workspace_is_visible(workspace)) { | ||
1025 | return false; | ||
1026 | } | ||
1007 | // Check view isn't in a tabbed or stacked container on an inactive tab | 1027 | // Check view isn't in a tabbed or stacked container on an inactive tab |
1008 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 1028 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
1009 | struct sway_container *con = view->container; | 1029 | struct sway_container *con = view->container; |
1010 | while (con) { | 1030 | while (con) { |
1011 | enum sway_container_layout layout = container_parent_layout(con); | 1031 | enum sway_container_layout layout = container_parent_layout(con); |
1012 | if (layout == L_TABBED || layout == L_STACKED) { | 1032 | if ((layout == L_TABBED || layout == L_STACKED) |
1033 | && !container_is_floating(con)) { | ||
1013 | struct sway_node *parent = con->parent ? | 1034 | struct sway_node *parent = con->parent ? |
1014 | &con->parent->node : &con->workspace->node; | 1035 | &con->parent->node : &con->workspace->node; |
1015 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { | 1036 | if (seat_get_active_tiling_child(seat, parent) != &con->node) { |
@@ -1023,10 +1044,6 @@ bool view_is_visible(struct sway_view *view) { | |||
1023 | !container_is_fullscreen_or_child(view->container)) { | 1044 | !container_is_fullscreen_or_child(view->container)) { |
1024 | return false; | 1045 | return false; |
1025 | } | 1046 | } |
1026 | // Check the workspace is visible | ||
1027 | if (!is_sticky) { | ||
1028 | return workspace_is_visible(workspace); | ||
1029 | } | ||
1030 | return true; | 1047 | return true; |
1031 | } | 1048 | } |
1032 | 1049 | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 16031e87..e9e5dfa2 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -20,17 +20,23 @@ | |||
20 | #include "log.h" | 20 | #include "log.h" |
21 | #include "util.h" | 21 | #include "util.h" |
22 | 22 | ||
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 |
@@ -62,6 +68,20 @@ struct sway_workspace *workspace_create(struct sway_output *output, | |||
62 | ws->output_priority = create_list(); | 68 | ws->output_priority = create_list(); |
63 | workspace_output_add_priority(ws, output); | 69 | workspace_output_add_priority(ws, output); |
64 | 70 | ||
71 | ws->gaps_outer = config->gaps_outer; | ||
72 | ws->gaps_inner = config->gaps_inner; | ||
73 | if (name) { | ||
74 | struct workspace_config *wsc = workspace_find_config(name); | ||
75 | if (wsc) { | ||
76 | if (wsc->gaps_outer != -1) { | ||
77 | ws->gaps_outer = wsc->gaps_outer; | ||
78 | } | ||
79 | if (wsc->gaps_inner != -1) { | ||
80 | ws->gaps_inner = wsc->gaps_inner; | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
65 | output_add_workspace(output, ws); | 85 | output_add_workspace(output, ws); |
66 | output_sort_workspaces(output); | 86 | output_sort_workspaces(output); |
67 | 87 | ||
@@ -121,17 +141,8 @@ void next_name_map(struct sway_container *ws, void *data) { | |||
121 | 141 | ||
122 | static bool workspace_valid_on_output(const char *output_name, | 142 | static bool workspace_valid_on_output(const char *output_name, |
123 | const char *ws_name) { | 143 | const char *ws_name) { |
124 | int i; | 144 | struct workspace_config *wsc = workspace_find_config(ws_name); |
125 | for (i = 0; i < config->workspace_outputs->length; ++i) { | 145 | return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0; |
126 | struct workspace_output *wso = config->workspace_outputs->items[i]; | ||
127 | if (strcasecmp(wso->workspace, ws_name) == 0) { | ||
128 | if (strcasecmp(wso->output, output_name) != 0) { | ||
129 | return false; | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return true; | ||
135 | } | 146 | } |
136 | 147 | ||
137 | static void workspace_name_from_binding(const struct sway_binding * binding, | 148 | static void workspace_name_from_binding(const struct sway_binding * binding, |
@@ -231,13 +242,13 @@ char *workspace_next_name(const char *output_name) { | |||
231 | workspace_name_from_binding(mode->keycode_bindings->items[i], | 242 | workspace_name_from_binding(mode->keycode_bindings->items[i], |
232 | output_name, &order, &target); | 243 | output_name, &order, &target); |
233 | } | 244 | } |
234 | for (int i = 0; i < config->workspace_outputs->length; ++i) { | 245 | for (int i = 0; i < config->workspace_configs->length; ++i) { |
235 | // Unlike with bindings, this does not guarantee order | 246 | // Unlike with bindings, this does not guarantee order |
236 | const struct workspace_output *wso = config->workspace_outputs->items[i]; | 247 | const struct workspace_config *wsc = config->workspace_configs->items[i]; |
237 | if (strcmp(wso->output, output_name) == 0 | 248 | if (wsc->output && strcmp(wsc->output, output_name) == 0 |
238 | && workspace_by_name(wso->workspace) == NULL) { | 249 | && workspace_by_name(wsc->workspace) == NULL) { |
239 | free(target); | 250 | free(target); |
240 | target = strdup(wso->workspace); | 251 | target = strdup(wsc->workspace); |
241 | break; | 252 | break; |
242 | } | 253 | } |
243 | } | 254 | } |
@@ -629,19 +640,25 @@ void workspace_add_gaps(struct sway_workspace *ws) { | |||
629 | if (ws->current_gaps > 0) { | 640 | if (ws->current_gaps > 0) { |
630 | return; | 641 | return; |
631 | } | 642 | } |
632 | bool should_apply = | 643 | if (!config->edge_gaps) { |
633 | config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1); | ||
634 | if (!should_apply) { | ||
635 | return; | 644 | return; |
636 | } | 645 | } |
646 | if (config->smart_gaps) { | ||
647 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
648 | struct sway_container *focus = | ||
649 | seat_get_focus_inactive_view(seat, &ws->node); | ||
650 | if (focus && focus->view && view_is_only_visible(focus->view)) { | ||
651 | return; | ||
652 | } | ||
653 | } | ||
637 | 654 | ||
638 | ws->current_gaps = ws->has_gaps ? ws->gaps_outer : config->gaps_outer; | 655 | ws->current_gaps = ws->gaps_outer; |
639 | 656 | ||
640 | if (ws->layout == L_TABBED || ws->layout == L_STACKED) { | 657 | if (ws->layout == L_TABBED || ws->layout == L_STACKED) { |
641 | // We have to add inner gaps for this, because children of tabbed and | 658 | // We have to add inner gaps for this, because children of tabbed and |
642 | // stacked containers don't apply their own gaps - they assume the | 659 | // stacked containers don't apply their own gaps - they assume the |
643 | // tabbed/stacked container is using gaps. | 660 | // tabbed/stacked container is using gaps. |
644 | ws->current_gaps += ws->has_gaps ? ws->gaps_inner : config->gaps_inner; | 661 | ws->current_gaps += ws->gaps_inner; |
645 | } | 662 | } |
646 | 663 | ||
647 | ws->x += ws->current_gaps; | 664 | ws->x += ws->current_gaps; |
diff --git a/swaybar/bar.c b/swaybar/bar.c index ab307fd4..388c24c4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c | |||
@@ -16,12 +16,13 @@ | |||
16 | #else | 16 | #else |
17 | #include <linux/input-event-codes.h> | 17 | #include <linux/input-event-codes.h> |
18 | #endif | 18 | #endif |
19 | #include "swaybar/render.h" | 19 | #include "swaybar/bar.h" |
20 | #include "swaybar/config.h" | 20 | #include "swaybar/config.h" |
21 | #include "swaybar/event_loop.h" | 21 | #include "swaybar/event_loop.h" |
22 | #include "swaybar/status_line.h" | 22 | #include "swaybar/i3bar.h" |
23 | #include "swaybar/bar.h" | ||
24 | #include "swaybar/ipc.h" | 23 | #include "swaybar/ipc.h" |
24 | #include "swaybar/status_line.h" | ||
25 | #include "swaybar/render.h" | ||
25 | #include "ipc-client.h" | 26 | #include "ipc-client.h" |
26 | #include "list.h" | 27 | #include "list.h" |
27 | #include "log.h" | 28 | #include "log.h" |
@@ -71,6 +72,16 @@ static void swaybar_output_free(struct swaybar_output *output) { | |||
71 | free(output); | 72 | free(output); |
72 | } | 73 | } |
73 | 74 | ||
75 | static void set_output_dirty(struct swaybar_output *output) { | ||
76 | if (output->frame_scheduled) { | ||
77 | output->dirty = true; | ||
78 | return; | ||
79 | } | ||
80 | if (output->surface) { | ||
81 | render_frame(output); | ||
82 | } | ||
83 | } | ||
84 | |||
74 | static void layer_surface_configure(void *data, | 85 | static void layer_surface_configure(void *data, |
75 | struct zwlr_layer_surface_v1 *surface, | 86 | struct zwlr_layer_surface_v1 *surface, |
76 | uint32_t serial, uint32_t width, uint32_t height) { | 87 | uint32_t serial, uint32_t width, uint32_t height) { |
@@ -78,7 +89,7 @@ static void layer_surface_configure(void *data, | |||
78 | output->width = width; | 89 | output->width = width; |
79 | output->height = height; | 90 | output->height = height; |
80 | zwlr_layer_surface_v1_ack_configure(surface, serial); | 91 | zwlr_layer_surface_v1_ack_configure(surface, serial); |
81 | render_frame(output->bar, output); | 92 | set_output_dirty(output); |
82 | } | 93 | } |
83 | 94 | ||
84 | static void layer_surface_closed(void *_output, | 95 | static void layer_surface_closed(void *_output, |
@@ -324,27 +335,22 @@ static void output_geometry(void *data, struct wl_output *wl_output, int32_t x, | |||
324 | const char *make, const char *model, int32_t transform) { | 335 | const char *make, const char *model, int32_t transform) { |
325 | struct swaybar_output *output = data; | 336 | struct swaybar_output *output = data; |
326 | output->subpixel = subpixel; | 337 | output->subpixel = subpixel; |
327 | if (output->surface) { | ||
328 | render_frame(output->bar, output); | ||
329 | } | ||
330 | } | 338 | } |
331 | 339 | ||
332 | static void output_mode(void *data, struct wl_output *output, uint32_t flags, | 340 | static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags, |
333 | int32_t width, int32_t height, int32_t refresh) { | 341 | int32_t width, int32_t height, int32_t refresh) { |
334 | // Who cares | 342 | // Who cares |
335 | } | 343 | } |
336 | 344 | ||
337 | static void output_done(void *data, struct wl_output *output) { | 345 | static void output_done(void *data, struct wl_output *wl_output) { |
338 | // Who cares | 346 | struct swaybar_output *output = data; |
347 | set_output_dirty(output); | ||
339 | } | 348 | } |
340 | 349 | ||
341 | static void output_scale(void *data, struct wl_output *wl_output, | 350 | static void output_scale(void *data, struct wl_output *wl_output, |
342 | int32_t factor) { | 351 | int32_t factor) { |
343 | struct swaybar_output *output = data; | 352 | struct swaybar_output *output = data; |
344 | output->scale = factor; | 353 | output->scale = factor; |
345 | if (output->surface) { | ||
346 | render_frame(output->bar, output); | ||
347 | } | ||
348 | } | 354 | } |
349 | 355 | ||
350 | struct wl_output_listener output_listener = { | 356 | struct wl_output_listener output_listener = { |
@@ -380,7 +386,7 @@ static void xdg_output_handle_done(void *data, | |||
380 | wl_list_insert(&bar->outputs, &output->link); | 386 | wl_list_insert(&bar->outputs, &output->link); |
381 | 387 | ||
382 | add_layer_surface(output); | 388 | add_layer_surface(output); |
383 | render_frame(bar, output); | 389 | set_output_dirty(output); |
384 | } | 390 | } |
385 | } | 391 | } |
386 | 392 | ||
@@ -469,23 +475,23 @@ static const struct wl_registry_listener registry_listener = { | |||
469 | .global_remove = handle_global_remove, | 475 | .global_remove = handle_global_remove, |
470 | }; | 476 | }; |
471 | 477 | ||
472 | static void render_all_frames(struct swaybar *bar) { | 478 | static void set_bar_dirty(struct swaybar *bar) { |
473 | struct swaybar_output *output; | 479 | struct swaybar_output *output; |
474 | wl_list_for_each(output, &bar->outputs, link) { | 480 | wl_list_for_each(output, &bar->outputs, link) { |
475 | if (output->surface != NULL) { | 481 | set_output_dirty(output); |
476 | render_frame(bar, output); | ||
477 | } | ||
478 | } | 482 | } |
479 | } | 483 | } |
480 | 484 | ||
481 | void bar_setup(struct swaybar *bar, | 485 | bool bar_setup(struct swaybar *bar, |
482 | const char *socket_path, const char *bar_id) { | 486 | const char *socket_path, const char *bar_id) { |
483 | bar_init(bar); | 487 | bar_init(bar); |
484 | init_event_loop(); | 488 | init_event_loop(); |
485 | 489 | ||
486 | bar->ipc_socketfd = ipc_open_socket(socket_path); | 490 | bar->ipc_socketfd = ipc_open_socket(socket_path); |
487 | bar->ipc_event_socketfd = ipc_open_socket(socket_path); | 491 | bar->ipc_event_socketfd = ipc_open_socket(socket_path); |
488 | ipc_initialize(bar, bar_id); | 492 | if (!ipc_initialize(bar, bar_id)) { |
493 | return false; | ||
494 | } | ||
489 | if (bar->config->status_command) { | 495 | if (bar->config->status_command) { |
490 | bar->status = status_line_init(bar->config->status_command); | 496 | bar->status = status_line_init(bar->config->status_command); |
491 | } | 497 | } |
@@ -525,7 +531,8 @@ void bar_setup(struct swaybar *bar, | |||
525 | assert(pointer->cursor_surface); | 531 | assert(pointer->cursor_surface); |
526 | 532 | ||
527 | ipc_get_workspaces(bar); | 533 | ipc_get_workspaces(bar); |
528 | render_all_frames(bar); | 534 | set_bar_dirty(bar); |
535 | return true; | ||
529 | } | 536 | } |
530 | 537 | ||
531 | static void display_in(int fd, short mask, void *data) { | 538 | static void display_in(int fd, short mask, void *data) { |
@@ -539,7 +546,7 @@ static void display_in(int fd, short mask, void *data) { | |||
539 | static void ipc_in(int fd, short mask, void *data) { | 546 | static void ipc_in(int fd, short mask, void *data) { |
540 | struct swaybar *bar = data; | 547 | struct swaybar *bar = data; |
541 | if (handle_ipc_readable(bar)) { | 548 | if (handle_ipc_readable(bar)) { |
542 | render_all_frames(bar); | 549 | set_bar_dirty(bar); |
543 | } | 550 | } |
544 | } | 551 | } |
545 | 552 | ||
@@ -547,10 +554,10 @@ static void status_in(int fd, short mask, void *data) { | |||
547 | struct swaybar *bar = data; | 554 | struct swaybar *bar = data; |
548 | if (mask & (POLLHUP | POLLERR)) { | 555 | if (mask & (POLLHUP | POLLERR)) { |
549 | status_error(bar->status, "[error reading from status command]"); | 556 | status_error(bar->status, "[error reading from status command]"); |
550 | render_all_frames(bar); | 557 | set_bar_dirty(bar); |
551 | remove_event(fd); | 558 | remove_event(fd); |
552 | } else if (status_handle_readable(bar->status)) { | 559 | } else if (status_handle_readable(bar->status)) { |
553 | render_all_frames(bar); | 560 | set_bar_dirty(bar); |
554 | } | 561 | } |
555 | } | 562 | } |
556 | 563 | ||
diff --git a/swaybar/config.c b/swaybar/config.c index db7b0db6..4e851cca 100644 --- a/swaybar/config.c +++ b/swaybar/config.c | |||
@@ -22,7 +22,7 @@ uint32_t parse_position(const char *position) { | |||
22 | } | 22 | } |
23 | } | 23 | } |
24 | 24 | ||
25 | struct swaybar_config *init_config() { | 25 | struct swaybar_config *init_config(void) { |
26 | struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); | 26 | struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); |
27 | config->status_command = NULL; | 27 | config->status_command = NULL; |
28 | config->pango_markup = false; | 28 | config->pango_markup = false; |
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 325aa61a..1d754808 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c | |||
@@ -6,7 +6,9 @@ | |||
6 | #include <string.h> | 6 | #include <string.h> |
7 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <wlr/util/log.h> | 8 | #include <wlr/util/log.h> |
9 | #include "swaybar/bar.h" | ||
9 | #include "swaybar/config.h" | 10 | #include "swaybar/config.h" |
11 | #include "swaybar/i3bar.h" | ||
10 | #include "swaybar/status_line.h" | 12 | #include "swaybar/status_line.h" |
11 | 13 | ||
12 | void i3bar_block_unref(struct i3bar_block *block) { | 14 | void i3bar_block_unref(struct i3bar_block *block) { |
@@ -258,7 +260,7 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
258 | 260 | ||
259 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 261 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
260 | struct i3bar_block *block, int x, int y, enum x11_button button) { | 262 | struct i3bar_block *block, int x, int y, enum x11_button button) { |
261 | wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); | 263 | wlr_log(WLR_DEBUG, "block %s clicked", block->name); |
262 | if (!block->name || !status->click_events) { | 264 | if (!block->name || !status->click_events) { |
263 | return HOTSPOT_PROCESS; | 265 | return HOTSPOT_PROCESS; |
264 | } | 266 | } |
@@ -274,10 +276,11 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | |||
274 | json_object_object_add(event_json, "button", json_object_new_int(button)); | 276 | json_object_object_add(event_json, "button", json_object_new_int(button)); |
275 | json_object_object_add(event_json, "x", json_object_new_int(x)); | 277 | json_object_object_add(event_json, "x", json_object_new_int(x)); |
276 | json_object_object_add(event_json, "y", json_object_new_int(y)); | 278 | json_object_object_add(event_json, "y", json_object_new_int(y)); |
277 | if (dprintf(status->write_fd, "%s,\n", | 279 | if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "", |
278 | json_object_to_json_string(event_json)) < 0) { | 280 | json_object_to_json_string(event_json)) < 0) { |
279 | status_error(status, "[failed to write click event]"); | 281 | status_error(status, "[failed to write click event]"); |
280 | } | 282 | } |
283 | status->clicked = true; | ||
281 | json_object_put(event_json); | 284 | json_object_put(event_json); |
282 | return HOTSPOT_IGNORE; | 285 | return HOTSPOT_IGNORE; |
283 | } | 286 | } |
diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 0e60c10c..7c53a44f 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c | |||
@@ -141,9 +141,16 @@ static void ipc_parse_colors( | |||
141 | } | 141 | } |
142 | } | 142 | } |
143 | 143 | ||
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/meson.build b/swaybar/meson.build index d65edb11..7a02a33f 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build | |||
@@ -24,5 +24,6 @@ executable( | |||
24 | wlroots, | 24 | wlroots, |
25 | ], | 25 | ], |
26 | link_with: [lib_sway_common, lib_sway_client], | 26 | link_with: [lib_sway_common, lib_sway_client], |
27 | install_rpath : rpathdir, | ||
27 | install: true | 28 | install: true |
28 | ) | 29 | ) |
diff --git a/swaybar/render.c b/swaybar/render.c index 9413dc57..dc31a5ea 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "pool-buffer.h" | 10 | #include "pool-buffer.h" |
11 | #include "swaybar/bar.h" | 11 | #include "swaybar/bar.h" |
12 | #include "swaybar/config.h" | 12 | #include "swaybar/config.h" |
13 | #include "swaybar/i3bar.h" | ||
13 | #include "swaybar/ipc.h" | 14 | #include "swaybar/ipc.h" |
14 | #include "swaybar/render.h" | 15 | #include "swaybar/render.h" |
15 | #include "swaybar/status_line.h" | 16 | #include "swaybar/status_line.h" |
@@ -20,47 +21,47 @@ static const double WS_VERTICAL_PADDING = 1.5; | |||
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,29 +454,41 @@ static uint32_t render_to_cairo(cairo_t *cairo, | |||
458 | */ | 454 | */ |
459 | double x = output->width * output->scale; | 455 | double x = output->width * output->scale; |
460 | if (bar->status) { | 456 | if (bar->status) { |
461 | uint32_t h = render_status_line(cairo, config, output, | 457 | uint32_t h = render_status_line(cairo, output, &x); |
462 | bar->status, output->focused, &x, output->height); | ||
463 | max_height = h > max_height ? h : max_height; | 458 | max_height = h > max_height ? h : max_height; |
464 | } | 459 | } |
465 | x = 0; | 460 | x = 0; |
466 | if (config->workspace_buttons) { | 461 | if (config->workspace_buttons) { |
467 | struct swaybar_workspace *ws; | 462 | struct swaybar_workspace *ws; |
468 | wl_list_for_each_reverse(ws, &output->workspaces, link) { | 463 | wl_list_for_each_reverse(ws, &output->workspaces, link) { |
469 | uint32_t h = render_workspace_button(cairo, | 464 | uint32_t h = render_workspace_button(cairo, output, ws, &x); |
470 | output, config, ws, &x, output->height); | ||
471 | max_height = h > max_height ? h : max_height; | 465 | max_height = h > max_height ? h : max_height; |
472 | } | 466 | } |
473 | } | 467 | } |
474 | if (config->binding_mode_indicator && config->mode) { | 468 | if (config->binding_mode_indicator && config->mode) { |
475 | uint32_t h = render_binding_mode_indicator(cairo, | 469 | uint32_t h = render_binding_mode_indicator(cairo, output, x); |
476 | output, config, config->mode, x, output->height); | ||
477 | max_height = h > max_height ? h : max_height; | 470 | max_height = h > max_height ? h : max_height; |
478 | } | 471 | } |
479 | 472 | ||
480 | return max_height > output->height ? max_height : output->height; | 473 | return max_height > output->height ? max_height : output->height; |
481 | } | 474 | } |
482 | 475 | ||
483 | void render_frame(struct swaybar *bar, struct swaybar_output *output) { | 476 | static void output_frame_handle_done(void *data, struct wl_callback *callback, |
477 | uint32_t time) { | ||
478 | wl_callback_destroy(callback); | ||
479 | struct swaybar_output *output = data; | ||
480 | output->frame_scheduled = false; | ||
481 | if (output->dirty) { | ||
482 | render_frame(output); | ||
483 | output->dirty = false; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static const struct wl_callback_listener output_frame_listener = { | ||
488 | .done = output_frame_handle_done | ||
489 | }; | ||
490 | |||
491 | void render_frame(struct swaybar_output *output) { | ||
484 | assert(output->surface != NULL); | 492 | assert(output->surface != NULL); |
485 | 493 | ||
486 | struct swaybar_hotspot *hotspot, *tmp; | 494 | struct swaybar_hotspot *hotspot, *tmp; |
@@ -506,9 +514,10 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { | |||
506 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 514 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); |
507 | cairo_paint(cairo); | 515 | cairo_paint(cairo); |
508 | cairo_restore(cairo); | 516 | cairo_restore(cairo); |
509 | uint32_t height = render_to_cairo(cairo, bar, output); | 517 | uint32_t height = render_to_cairo(cairo, output); |
510 | if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { | 518 | int config_height = output->bar->config->height; |
511 | height = bar->config->height; | 519 | if (config_height >= 0 && height < (uint32_t)config_height) { |
520 | height = config_height; | ||
512 | } | 521 | } |
513 | if (height != output->height) { | 522 | if (height != output->height) { |
514 | // Reconfigure surface | 523 | // Reconfigure surface |
@@ -519,11 +528,13 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { | |||
519 | wl_surface_commit(output->surface); | 528 | wl_surface_commit(output->surface); |
520 | } else if (height > 0) { | 529 | } else if (height > 0) { |
521 | // Replay recording into shm and send it off | 530 | // Replay recording into shm and send it off |
522 | output->current_buffer = get_next_buffer(bar->shm, | 531 | output->current_buffer = get_next_buffer(output->bar->shm, |
523 | output->buffers, | 532 | output->buffers, |
524 | output->width * output->scale, | 533 | output->width * output->scale, |
525 | output->height * output->scale); | 534 | output->height * output->scale); |
526 | if (!output->current_buffer) { | 535 | if (!output->current_buffer) { |
536 | cairo_surface_destroy(recorder); | ||
537 | cairo_destroy(cairo); | ||
527 | return; | 538 | return; |
528 | } | 539 | } |
529 | cairo_t *shm = output->current_buffer->cairo; | 540 | cairo_t *shm = output->current_buffer->cairo; |
@@ -541,6 +552,11 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { | |||
541 | output->current_buffer->buffer, 0, 0); | 552 | output->current_buffer->buffer, 0, 0); |
542 | wl_surface_damage(output->surface, 0, 0, | 553 | wl_surface_damage(output->surface, 0, 0, |
543 | output->width, output->height); | 554 | output->width, output->height); |
555 | |||
556 | struct wl_callback *frame_callback = wl_surface_frame(output->surface); | ||
557 | wl_callback_add_listener(frame_callback, &output_frame_listener, output); | ||
558 | output->frame_scheduled = true; | ||
559 | |||
544 | wl_surface_commit(output->surface); | 560 | wl_surface_commit(output->surface); |
545 | } | 561 | } |
546 | cairo_surface_destroy(recorder); | 562 | cairo_surface_destroy(recorder); |
diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 48b43248..ed6dc7c8 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c | |||
@@ -1,12 +1,15 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <fcntl.h> | 2 | #include <fcntl.h> |
3 | #include <sys/ioctl.h> | ||
3 | #include <json-c/json.h> | 4 | #include <json-c/json.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <string.h> | 6 | #include <string.h> |
6 | #include <stdio.h> | 7 | #include <stdio.h> |
7 | #include <unistd.h> | 8 | #include <unistd.h> |
8 | #include <wlr/util/log.h> | 9 | #include <wlr/util/log.h> |
10 | #include "swaybar/bar.h" | ||
9 | #include "swaybar/config.h" | 11 | #include "swaybar/config.h" |
12 | #include "swaybar/i3bar.h" | ||
10 | #include "swaybar/event_loop.h" | 13 | #include "swaybar/event_loop.h" |
11 | #include "swaybar/status_line.h" | 14 | #include "swaybar/status_line.h" |
12 | #include "readline.h" | 15 | #include "readline.h" |
@@ -34,18 +37,35 @@ bool status_handle_readable(struct status_line *status) { | |||
34 | switch (status->protocol) { | 37 | switch (status->protocol) { |
35 | case PROTOCOL_UNDEF: | 38 | case PROTOCOL_UNDEF: |
36 | errno = 0; | 39 | errno = 0; |
37 | read_bytes = getline(&status->buffer, | 40 | int available_bytes; |
38 | &status->buffer_size, status->read); | 41 | if (ioctl(status->read_fd, FIONREAD, &available_bytes) == -1) { |
39 | if (errno == EAGAIN) { | 42 | wlr_log(WLR_ERROR, "Unable to read status command output size"); |
40 | clearerr(status->read); | ||
41 | } else if (errno) { | ||
42 | status_error(status, "[error reading from status command]"); | 43 | status_error(status, "[error reading from status command]"); |
43 | return true; | 44 | return true; |
44 | } | 45 | } |
45 | 46 | ||
47 | if ((size_t)available_bytes + 1 > status->buffer_size) { | ||
48 | // need room for leading '\0' too | ||
49 | status->buffer_size = available_bytes + 1; | ||
50 | status->buffer = realloc(status->buffer, status->buffer_size); | ||
51 | } | ||
52 | if (status->buffer == NULL) { | ||
53 | wlr_log_errno(WLR_ERROR, "Unable to read status line"); | ||
54 | status_error(status, "[error reading from status command]"); | ||
55 | return true; | ||
56 | } | ||
57 | |||
58 | read_bytes = read(status->read_fd, status->buffer, available_bytes); | ||
59 | if (read_bytes != available_bytes) { | ||
60 | status_error(status, "[error reading from status command]"); | ||
61 | return true; | ||
62 | } | ||
63 | status->buffer[available_bytes] = 0; | ||
64 | |||
46 | // the header must be sent completely the first time round | 65 | // the header must be sent completely the first time round |
66 | char *newline = strchr(status->buffer, '\n'); | ||
47 | json_object *header, *version; | 67 | json_object *header, *version; |
48 | if (status->buffer[read_bytes - 1] == '\n' | 68 | if (newline != NULL |
49 | && (header = json_tokener_parse(status->buffer)) | 69 | && (header = json_tokener_parse(status->buffer)) |
50 | && json_object_object_get_ex(header, "version", &version) | 70 | && json_object_object_get_ex(header, "version", &version) |
51 | && json_object_get_int(version) == 1) { | 71 | && json_object_get_int(version) == 1) { |
@@ -67,13 +87,9 @@ bool status_handle_readable(struct status_line *status) { | |||
67 | 87 | ||
68 | wl_list_init(&status->blocks); | 88 | wl_list_init(&status->blocks); |
69 | status->tokener = json_tokener_new(); | 89 | status->tokener = json_tokener_new(); |
70 | read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read); | 90 | status->buffer_index = strlen(newline + 1); |
71 | if (read_bytes > 0) { | 91 | memmove(status->buffer, newline + 1, status->buffer_index + 1); |
72 | status->buffer_index = read_bytes; | 92 | return i3bar_handle_readable(status); |
73 | return i3bar_handle_readable(status); | ||
74 | } else { | ||
75 | return false; | ||
76 | } | ||
77 | } | 93 | } |
78 | 94 | ||
79 | wlr_log(WLR_DEBUG, "Using text protocol."); | 95 | wlr_log(WLR_DEBUG, "Using text protocol."); |
diff --git a/swaybg/meson.build b/swaybg/meson.build index 8704de6d..095c5488 100644 --- a/swaybg/meson.build +++ b/swaybg/meson.build | |||
@@ -14,5 +14,6 @@ executable( | |||
14 | wlroots, | 14 | wlroots, |
15 | ], | 15 | ], |
16 | link_with: [lib_sway_common, lib_sway_client], | 16 | link_with: [lib_sway_common, lib_sway_client], |
17 | install_rpath : rpathdir, | ||
17 | install: true | 18 | install: true |
18 | ) | 19 | ) |
diff --git a/swayidle/main.c b/swayidle/main.c index 678d622f..5b6c95a7 100644 --- a/swayidle/main.c +++ b/swayidle/main.c | |||
@@ -92,7 +92,7 @@ static int release_lock(void *data) { | |||
92 | return 0; | 92 | return 0; |
93 | } | 93 | } |
94 | 94 | ||
95 | void acquire_sleep_lock() { | 95 | void acquire_sleep_lock(void) { |
96 | sd_bus_message *msg = NULL; | 96 | sd_bus_message *msg = NULL; |
97 | sd_bus_error error = SD_BUS_ERROR_NULL; | 97 | sd_bus_error error = SD_BUS_ERROR_NULL; |
98 | struct sd_bus *bus; | 98 | struct sd_bus *bus; |
@@ -161,7 +161,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) { | |||
161 | return 1; | 161 | return 1; |
162 | } | 162 | } |
163 | 163 | ||
164 | void setup_sleep_listener() { | 164 | void setup_sleep_listener(void) { |
165 | struct sd_bus *bus; | 165 | struct sd_bus *bus; |
166 | 166 | ||
167 | int ret = sd_bus_default_system(&bus); | 167 | int ret = sd_bus_default_system(&bus); |
diff --git a/swayidle/meson.build b/swayidle/meson.build index f62545f8..6c3ac119 100644 --- a/swayidle/meson.build +++ b/swayidle/meson.build | |||
@@ -14,5 +14,6 @@ executable( | |||
14 | swayidle_deps, | 14 | swayidle_deps, |
15 | ], | 15 | ], |
16 | link_with: [lib_sway_common, lib_sway_client], | 16 | link_with: [lib_sway_common, lib_sway_client], |
17 | install_rpath : rpathdir, | ||
17 | install: true | 18 | install: true |
18 | ) | 19 | ) |
diff --git a/swaylock/main.c b/swaylock/main.c index c25c8eec..ed8c5607 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -32,7 +32,7 @@ void sway_terminate(int exit_code) { | |||
32 | exit(exit_code); | 32 | exit(exit_code); |
33 | } | 33 | } |
34 | 34 | ||
35 | static void daemonize() { | 35 | static void daemonize(void) { |
36 | int fds[2]; | 36 | int fds[2]; |
37 | if (pipe(fds) != 0) { | 37 | if (pipe(fds) != 0) { |
38 | wlr_log(WLR_ERROR, "Failed to pipe"); | 38 | wlr_log(WLR_ERROR, "Failed to pipe"); |
@@ -845,6 +845,9 @@ static int load_config(char *path, struct swaylock_state *state, | |||
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..6605340b 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build | |||
@@ -1,26 +1,39 @@ | |||
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], |
36 | install_rpath : rpathdir, | ||
24 | install: true | 37 | install: true |
25 | ) | 38 | ) |
26 | 39 | ||
diff --git a/swaylock/pam.c b/swaylock/pam.c new file mode 100644 index 00000000..cac95a85 --- /dev/null +++ b/swaylock/pam.c | |||
@@ -0,0 +1,62 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <pwd.h> | ||
3 | #include <security/pam_appl.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <unistd.h> | ||
8 | #include <wlr/util/log.h> | ||
9 | #include "swaylock/swaylock.h" | ||
10 | |||
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 | } | ||
diff --git a/swaymsg/meson.build b/swaymsg/meson.build index 8638b838..7318349d 100644 --- a/swaymsg/meson.build +++ b/swaymsg/meson.build | |||
@@ -4,5 +4,6 @@ executable( | |||
4 | include_directories: [sway_inc], | 4 | include_directories: [sway_inc], |
5 | dependencies: [jsonc, wlroots], | 5 | dependencies: [jsonc, wlroots], |
6 | link_with: [lib_sway_common], | 6 | link_with: [lib_sway_common], |
7 | install_rpath : rpathdir, | ||
7 | install: true | 8 | install: true |
8 | ) | 9 | ) |
diff --git a/swaynag/config.c b/swaynag/config.c index 4d0824c9..cd34dcc2 100644 --- a/swaynag/config.c +++ b/swaynag/config.c | |||
@@ -11,7 +11,7 @@ | |||
11 | #include "util.h" | 11 | #include "util.h" |
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
13 | 13 | ||
14 | static char *read_from_stdin() { | 14 | static char *read_from_stdin(void) { |
15 | char *buffer = NULL; | 15 | char *buffer = NULL; |
16 | while (!feof(stdin)) { | 16 | while (!feof(stdin)) { |
17 | char *line = read_line(stdin); | 17 | char *line = read_line(stdin); |
diff --git a/swaynag/meson.build b/swaynag/meson.build index 2ba3ed95..223a0bc7 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build | |||
@@ -19,5 +19,6 @@ executable( | |||
19 | wlroots, | 19 | wlroots, |
20 | ], | 20 | ], |
21 | link_with: [lib_sway_common, lib_sway_client], | 21 | link_with: [lib_sway_common, lib_sway_client], |
22 | install_rpath : rpathdir, | ||
22 | install: true | 23 | install: true |
23 | ) | 24 | ) |