aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--completions/bash/swayidle48
-rw-r--r--completions/fish/swayidle.fish3
-rw-r--r--completions/zsh/_swayidle22
-rw-r--r--include/sway/commands.h2
-rw-r--r--include/sway/config.h2
-rw-r--r--include/sway/input/cursor.h6
-rw-r--r--include/sway/input/input-manager.h4
-rw-r--r--include/sway/input/seat.h86
-rw-r--r--include/sway/output.h8
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/swaybar/bar.h1
-rw-r--r--include/swaybar/config.h2
-rw-r--r--include/swaylock/swaylock.h5
-rw-r--r--meson.build4
-rw-r--r--sway/commands/bar.c2
-rw-r--r--sway/commands/bar/status_edge_padding.c21
-rw-r--r--sway/commands/bar/status_padding.c21
-rw-r--r--sway/commands/input/events.c76
-rw-r--r--sway/commands/input/scroll_button.c34
-rw-r--r--sway/config.c16
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/desktop/output.c2
-rw-r--r--sway/desktop/render.c27
-rw-r--r--sway/desktop/transaction.c2
-rw-r--r--sway/desktop/xdg_shell.c4
-rw-r--r--sway/desktop/xdg_shell_v6.c4
-rw-r--r--sway/desktop/xwayland.c4
-rw-r--r--sway/input/cursor.c398
-rw-r--r--sway/input/input-manager.c172
-rw-r--r--sway/input/seat.c273
-rw-r--r--sway/input/seatop_down.c77
-rw-r--r--sway/input/seatop_move_floating.c65
-rw-r--r--sway/input/seatop_move_tiling.c335
-rw-r--r--sway/input/seatop_resize_floating.c199
-rw-r--r--sway/input/seatop_resize_tiling.c92
-rw-r--r--sway/ipc-json.c25
-rw-r--r--sway/meson.build7
-rw-r--r--sway/sway-bar.5.scd12
-rw-r--r--sway/sway-input.5.scd18
-rw-r--r--sway/tree/container.c23
-rw-r--r--swaybar/bar.c30
-rw-r--r--swaybar/config.c2
-rw-r--r--swaybar/ipc.c11
-rw-r--r--swaybar/render.c37
-rw-r--r--swaybar/tray/tray.c8
-rw-r--r--swayidle/main.c483
-rw-r--r--swayidle/meson.build27
-rw-r--r--swayidle/swayidle.1.scd63
-rw-r--r--swaylock/main.c153
-rw-r--r--swaylock/render.c27
-rw-r--r--swaylock/swaylock.1.scd25
-rw-r--r--swaymsg/main.c6
52 files changed, 1586 insertions, 1394 deletions
diff --git a/completions/bash/swayidle b/completions/bash/swayidle
deleted file mode 100644
index a0cdc8b2..00000000
--- a/completions/bash/swayidle
+++ /dev/null
@@ -1,48 +0,0 @@
1# swaymsg(1) completion
2
3_swayidle()
4{
5 local cur prev
6 _get_comp_words_by_ref -n : cur prev
7 local prev2=${COMP_WORDS[COMP_CWORD-2]}
8 local prev3=${COMP_WORDS[COMP_CWORD-3]}
9
10 events=(
11 'timeout'
12 'before-sleep'
13 )
14
15 short=(
16 -h
17 -d
18 )
19
20 if [ "$prev" = timeout ]; then
21 # timeout <timeout>
22 return
23 elif [ "$prev2" = timeout ]; then
24 # timeout <timeout> <timeout command>
25 COMPREPLY=($(compgen -c -- "$cur"))
26 return
27 elif [ "$prev3" = timeout ]; then
28 # timeout <timeout> <timeout command> [resume <resume command>]
29 COMPREPLY=(resume)
30 # optional argument; no return here as user may skip 'resume'
31 fi
32
33 case "$prev" in
34 resume)
35 COMPREPLY=($(compgen -c -- "$cur"))
36 return
37 ;;
38 before-sleep)
39 COMPREPLY=($(compgen -c -- "$cur"))
40 return
41 ;;
42 esac
43
44 COMPREPLY+=($(compgen -W "${events[*]}" -- "$cur"))
45 COMPREPLY+=($(compgen -W "${short[*]}" -- "$cur"))
46
47} &&
48complete -F _swayidle swayidle
diff --git a/completions/fish/swayidle.fish b/completions/fish/swayidle.fish
deleted file mode 100644
index 71279925..00000000
--- a/completions/fish/swayidle.fish
+++ /dev/null
@@ -1,3 +0,0 @@
1# swayidle
2complete -c swayidle -s h --description 'show help'
3complete -c swayidle -s d --description 'debug'
diff --git a/completions/zsh/_swayidle b/completions/zsh/_swayidle
deleted file mode 100644
index b419bc2c..00000000
--- a/completions/zsh/_swayidle
+++ /dev/null
@@ -1,22 +0,0 @@
1#compdef swayidle
2#
3# Completion script for swayidle
4#
5
6local events=('timeout:Execute timeout command if there is no activity for timeout seconds'
7 'before-sleep:Execute before-sleep command before sleep')
8local resume=('resume:Execute command when there is activity again')
9
10if (($#words <= 2)); then
11 _arguments -C \
12 '(-h --help)'{-h,--help}'[Show help message and quit]' \
13 '(-d)'-d'[Enable debug output]'
14 _describe -t "events" 'swayidle' events
15
16elif [[ "$words[-3]" == before-sleep || "$words[-3]" == resume ]]; then
17 _describe -t "events" 'swayidle' events
18
19elif [[ "$words[-4]" == timeout ]]; then
20 _describe -t "events" 'swayidle' events
21 _describe -t "resume" 'swayidle' resume
22fi
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 7d0ff838..04f93ba9 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -198,6 +198,8 @@ sway_cmd bar_cmd_id;
198sway_cmd bar_cmd_position; 198sway_cmd bar_cmd_position;
199sway_cmd bar_cmd_separator_symbol; 199sway_cmd bar_cmd_separator_symbol;
200sway_cmd bar_cmd_status_command; 200sway_cmd bar_cmd_status_command;
201sway_cmd bar_cmd_status_edge_padding;
202sway_cmd bar_cmd_status_padding;
201sway_cmd bar_cmd_pango_markup; 203sway_cmd bar_cmd_pango_markup;
202sway_cmd bar_cmd_strip_workspace_numbers; 204sway_cmd bar_cmd_strip_workspace_numbers;
203sway_cmd bar_cmd_strip_workspace_name; 205sway_cmd bar_cmd_strip_workspace_name;
diff --git a/include/sway/config.h b/include/sway/config.h
index 29c21afe..a667984d 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -232,6 +232,8 @@ struct bar_config {
232 bool verbose; 232 bool verbose;
233 struct side_gaps gaps; 233 struct side_gaps gaps;
234 pid_t pid; 234 pid_t pid;
235 int status_padding;
236 int status_edge_padding;
235 struct { 237 struct {
236 char *background; 238 char *background;
237 char *statusline; 239 char *statusline;
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index e6477b4a..77aa0ea1 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -50,6 +50,12 @@ struct sway_cursor {
50 size_t pressed_button_count; 50 size_t pressed_button_count;
51}; 51};
52 52
53struct sway_node;
54
55struct sway_node *node_at_coords(
56 struct sway_seat *seat, double lx, double ly,
57 struct wlr_surface **surface, double *sx, double *sy);
58
53void sway_cursor_destroy(struct sway_cursor *cursor); 59void sway_cursor_destroy(struct sway_cursor *cursor);
54struct sway_cursor *sway_cursor_create(struct sway_seat *seat); 60struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
55 61
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 08e749dc..8e8bf1f2 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -37,6 +37,10 @@ void input_manager_configure_xcursor(void);
37 37
38void input_manager_apply_input_config(struct input_config *input_config); 38void input_manager_apply_input_config(struct input_config *input_config);
39 39
40void input_manager_reset_input(struct sway_input_device *input_device);
41
42void input_manager_reset_all_inputs();
43
40void input_manager_apply_seat_config(struct seat_config *seat_config); 44void input_manager_apply_seat_config(struct seat_config *seat_config);
41 45
42struct sway_seat *input_manager_get_default_seat(void); 46struct sway_seat *input_manager_get_default_seat(void);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index a3c20346..d2f14895 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -6,6 +6,17 @@
6#include <wlr/util/edges.h> 6#include <wlr/util/edges.h>
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8 8
9struct sway_seat;
10
11struct sway_seatop_impl {
12 void (*motion)(struct sway_seat *seat, uint32_t time_msec);
13 void (*finish)(struct sway_seat *seat);
14 void (*abort)(struct sway_seat *seat);
15 void (*unref)(struct sway_seat *seat, struct sway_container *con);
16 void (*render)(struct sway_seat *seat, struct sway_output *output,
17 pixman_region32_t *damage);
18};
19
9struct sway_seat_device { 20struct sway_seat_device {
10 struct sway_seat *sway_seat; 21 struct sway_seat *sway_seat;
11 struct sway_input_device *input_device; 22 struct sway_input_device *input_device;
@@ -35,16 +46,6 @@ struct sway_drag_icon {
35 struct wl_listener destroy; 46 struct wl_listener destroy;
36}; 47};
37 48
38enum sway_seat_operation {
39 OP_NONE,
40 OP_DOWN,
41 OP_MOVE_FLOATING,
42 OP_MOVE_TILING_THRESHOLD,
43 OP_MOVE_TILING,
44 OP_RESIZE_FLOATING,
45 OP_RESIZE_TILING,
46};
47
48struct sway_seat { 49struct sway_seat {
49 struct wlr_seat *wlr_seat; 50 struct wlr_seat *wlr_seat;
50 struct sway_cursor *cursor; 51 struct sway_cursor *cursor;
@@ -64,19 +65,10 @@ struct sway_seat {
64 int32_t touch_id; 65 int32_t touch_id;
65 double touch_x, touch_y; 66 double touch_x, touch_y;
66 67
67 // Operations (drag and resize) 68 // Seat operations (drag and resize)
68 enum sway_seat_operation operation; 69 const struct sway_seatop_impl *seatop_impl;
69 struct sway_container *op_container; 70 void *seatop_data;
70 struct sway_node *op_target_node; // target for tiling move 71 uint32_t seatop_button;
71 enum wlr_edges op_target_edge;
72 struct wlr_box op_drop_box;
73 enum wlr_edges op_resize_edge;
74 uint32_t op_button;
75 bool op_resize_preserve_ratio;
76 double op_ref_lx, op_ref_ly; // cursor's x/y at start of op
77 double op_ref_width, op_ref_height; // container's size at start of op
78 double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op
79 bool op_moved; // if the mouse moved during a down op
80 72
81 uint32_t last_button; 73 uint32_t last_button;
82 uint32_t last_button_serial; 74 uint32_t last_button_serial;
@@ -100,6 +92,9 @@ void seat_add_device(struct sway_seat *seat,
100void seat_configure_device(struct sway_seat *seat, 92void seat_configure_device(struct sway_seat *seat,
101 struct sway_input_device *device); 93 struct sway_input_device *device);
102 94
95void seat_reset_device(struct sway_seat *seat,
96 struct sway_input_device *input_device);
97
103void seat_remove_device(struct sway_seat *seat, 98void seat_remove_device(struct sway_seat *seat,
104 struct sway_input_device *device); 99 struct sway_input_device *device);
105 100
@@ -181,32 +176,59 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
181 176
182void drag_icon_update_position(struct sway_drag_icon *icon); 177void drag_icon_update_position(struct sway_drag_icon *icon);
183 178
184void seat_begin_down(struct sway_seat *seat, struct sway_container *con, 179void seatop_begin_down(struct sway_seat *seat,
185 uint32_t button, double sx, double sy); 180 struct sway_container *con, uint32_t button, int sx, int sy);
186 181
187void seat_begin_move_floating(struct sway_seat *seat, 182void seatop_begin_move_floating(struct sway_seat *seat,
188 struct sway_container *con, uint32_t button); 183 struct sway_container *con, uint32_t button);
189 184
190void seat_begin_move_tiling_threshold(struct sway_seat *seat, 185void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
191 struct sway_container *con, uint32_t button); 186 struct sway_container *con, uint32_t button);
192 187
193void seat_begin_move_tiling(struct sway_seat *seat, 188void seatop_begin_move_tiling(struct sway_seat *seat,
194 struct sway_container *con, uint32_t button); 189 struct sway_container *con, uint32_t button);
195 190
196void seat_begin_resize_floating(struct sway_seat *seat, 191void seatop_begin_resize_floating(struct sway_seat *seat,
197 struct sway_container *con, uint32_t button, enum wlr_edges edge); 192 struct sway_container *con, uint32_t button, enum wlr_edges edge);
198 193
199void seat_begin_resize_tiling(struct sway_seat *seat, 194void seatop_begin_resize_tiling(struct sway_seat *seat,
200 struct sway_container *con, uint32_t button, enum wlr_edges edge); 195 struct sway_container *con, uint32_t button, enum wlr_edges edge);
201 196
202struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 197struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
203 struct sway_workspace *workspace); 198 struct sway_workspace *workspace);
204 199
205void seat_end_mouse_operation(struct sway_seat *seat);
206
207void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 200void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
208 uint32_t button, enum wlr_button_state state); 201 uint32_t button, enum wlr_button_state state);
209 202
210void seat_consider_warp_to_focus(struct sway_seat *seat); 203void seat_consider_warp_to_focus(struct sway_seat *seat);
211 204
205bool seat_doing_seatop(struct sway_seat *seat);
206
207void seatop_motion(struct sway_seat *seat, uint32_t time_msec);
208
209/**
210 * End a seatop and apply the affects.
211 */
212void seatop_finish(struct sway_seat *seat);
213
214/**
215 * End a seatop without applying the affects.
216 */
217void seatop_abort(struct sway_seat *seat);
218
219/**
220 * Instructs the seatop implementation to drop any references to the given
221 * container (eg. because the container is destroying).
222 * The seatop may choose to abort itself in response to this.
223 */
224void seatop_unref(struct sway_seat *seat, struct sway_container *con);
225
226/**
227 * Instructs a seatop to render anything that it needs to render
228 * (eg. dropzone for move-tiling)
229 */
230void seatop_render(struct sway_seat *seat, struct sway_output *output,
231 pixman_region32_t *damage);
232
233
212#endif 234#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index f7c5ceba..bdf9614d 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -146,4 +146,12 @@ enum sway_container_layout output_get_default_layout(
146 146
147void output_add_listeners(struct sway_output *output); 147void output_add_listeners(struct sway_output *output);
148 148
149void render_rect(struct wlr_output *wlr_output,
150 pixman_region32_t *output_damage, const struct wlr_box *_box,
151 float color[static 4]);
152
153void premultiply_alpha(float color[4], float opacity);
154
155void scale_box(struct wlr_box *box, float scale);
156
149#endif 157#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 1d0a0ad1..9a432cb2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -333,4 +333,6 @@ void container_add_mark(struct sway_container *container, char *mark);
333 333
334void container_update_marks_textures(struct sway_container *container); 334void container_update_marks_textures(struct sway_container *container);
335 335
336void container_raise_floating(struct sway_container *con);
337
336#endif 338#endif
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index e377b8de..2d9ba0d9 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -61,6 +61,7 @@ struct swaybar_output {
61 struct wl_list hotspots; // swaybar_hotspot::link 61 struct wl_list hotspots; // swaybar_hotspot::link
62 62
63 char *name; 63 char *name;
64 char *identifier;
64 bool focused; 65 bool focused;
65 66
66 uint32_t width, height; 67 uint32_t width, height;
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 1f6577bd..add0a1cf 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -43,6 +43,8 @@ struct swaybar_config {
43 struct wl_list outputs; // config_output::link 43 struct wl_list outputs; // config_output::link
44 bool all_outputs; 44 bool all_outputs;
45 int height; 45 int height;
46 int status_padding;
47 int status_edge_padding;
46 struct { 48 struct {
47 int top; 49 int top;
48 int right; 50 int right;
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index 18af7ab4..516a56f4 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -22,6 +22,7 @@ enum auth_state {
22struct swaylock_colorset { 22struct swaylock_colorset {
23 uint32_t input; 23 uint32_t input;
24 uint32_t cleared; 24 uint32_t cleared;
25 uint32_t caps_lock;
25 uint32_t verifying; 26 uint32_t verifying;
26 uint32_t wrong; 27 uint32_t wrong;
27}; 28};
@@ -30,6 +31,8 @@ struct swaylock_colors {
30 uint32_t background; 31 uint32_t background;
31 uint32_t bs_highlight; 32 uint32_t bs_highlight;
32 uint32_t key_highlight; 33 uint32_t key_highlight;
34 uint32_t caps_lock_bs_highlight;
35 uint32_t caps_lock_key_highlight;
33 uint32_t separator; 36 uint32_t separator;
34 struct swaylock_colorset inside; 37 struct swaylock_colorset inside;
35 struct swaylock_colorset line; 38 struct swaylock_colorset line;
@@ -45,6 +48,8 @@ struct swaylock_args {
45 uint32_t thickness; 48 uint32_t thickness;
46 bool ignore_empty; 49 bool ignore_empty;
47 bool show_indicator; 50 bool show_indicator;
51 bool show_caps_lock_text;
52 bool show_caps_lock_indicator;
48 bool daemonize; 53 bool daemonize;
49}; 54};
50 55
diff --git a/meson.build b/meson.build
index b1353b59..bffbe312 100644
--- a/meson.build
+++ b/meson.build
@@ -86,7 +86,6 @@ if scdoc.found()
86 'sway/sway-output.5.scd', 86 'sway/sway-output.5.scd',
87 'swaylock/swaylock.1.scd', 87 'swaylock/swaylock.1.scd',
88 'swaymsg/swaymsg.1.scd', 88 'swaymsg/swaymsg.1.scd',
89 'swayidle/swayidle.1.scd',
90 'swaynag/swaynag.1.scd', 89 'swaynag/swaynag.1.scd',
91 'swaynag/swaynag.5.scd', 90 'swaynag/swaynag.5.scd',
92 ] 91 ]
@@ -146,7 +145,6 @@ subdir('swaymsg')
146subdir('client') 145subdir('client')
147subdir('swaybg') 146subdir('swaybg')
148subdir('swaybar') 147subdir('swaybar')
149subdir('swayidle')
150subdir('swaynag') 148subdir('swaynag')
151subdir('swaylock') 149subdir('swaylock')
152 150
@@ -214,7 +212,6 @@ if (get_option('bash-completions'))
214 bash_files = files( 212 bash_files = files(
215 'completions/bash/sway', 213 'completions/bash/sway',
216 'completions/bash/swaybar', 214 'completions/bash/swaybar',
217 'completions/bash/swayidle',
218 'completions/bash/swaylock', 215 'completions/bash/swaylock',
219 'completions/bash/swaymsg', 216 'completions/bash/swaymsg',
220 ) 217 )
@@ -226,7 +223,6 @@ endif
226if (get_option('fish-completions')) 223if (get_option('fish-completions'))
227 fish_files = files( 224 fish_files = files(
228 'completions/fish/sway.fish', 225 'completions/fish/sway.fish',
229 'completions/fish/swayidle.fish',
230 'completions/fish/swaylock.fish', 226 'completions/fish/swaylock.fish',
231 'completions/fish/swaymsg.fish', 227 'completions/fish/swaymsg.fish',
232 'completions/fish/swaynag.fish', 228 'completions/fish/swaynag.fish',
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
index 507ee10a..3e7c1b0f 100644
--- a/sway/commands/bar.c
+++ b/sway/commands/bar.c
@@ -23,6 +23,8 @@ static struct cmd_handler bar_handlers[] = {
23 { "position", bar_cmd_position }, 23 { "position", bar_cmd_position },
24 { "separator_symbol", bar_cmd_separator_symbol }, 24 { "separator_symbol", bar_cmd_separator_symbol },
25 { "status_command", bar_cmd_status_command }, 25 { "status_command", bar_cmd_status_command },
26 { "status_edge_padding", bar_cmd_status_edge_padding },
27 { "status_padding", bar_cmd_status_padding },
26 { "strip_workspace_name", bar_cmd_strip_workspace_name }, 28 { "strip_workspace_name", bar_cmd_strip_workspace_name },
27 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, 29 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
28 { "tray_bindsym", bar_cmd_tray_bindsym }, 30 { "tray_bindsym", bar_cmd_tray_bindsym },
diff --git a/sway/commands/bar/status_edge_padding.c b/sway/commands/bar/status_edge_padding.c
new file mode 100644
index 00000000..f3b10631
--- /dev/null
+++ b/sway/commands/bar/status_edge_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_edge_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_edge_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_edge_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_edge_padding = padding;
18 wlr_log(WLR_DEBUG, "Status edge padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_edge_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/status_padding.c b/sway/commands/bar/status_padding.c
new file mode 100644
index 00000000..13b8eb6b
--- /dev/null
+++ b/sway/commands/bar/status_padding.c
@@ -0,0 +1,21 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_status_padding(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_padding", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 char *end;
12 int padding = strtol(argv[0], &end, 10);
13 if (strlen(end) || padding < 0) {
14 return cmd_results_new(CMD_INVALID, "status_padding",
15 "Padding must be a positive integer");
16 }
17 config->current_bar->status_padding = padding;
18 wlr_log(WLR_DEBUG, "Status padding on bar %s: %d",
19 config->current_bar->id, config->current_bar->status_padding);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index e7ed69c6..69f46269 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,10 +1,69 @@
1#include <limits.h>
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
4#include <wlr/backend/libinput.h>
3#include "sway/config.h" 5#include "sway/config.h"
4#include "sway/commands.h" 6#include "sway/commands.h"
5#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
6#include "log.h" 8#include "log.h"
7 9
10static void toggle_send_events_for_device(struct input_config *ic,
11 struct sway_input_device *input_device) {
12 struct wlr_input_device *wlr_device = input_device->wlr_device;
13 if (!wlr_input_device_is_libinput(wlr_device)) {
14 return;
15 }
16 struct libinput_device *libinput_dev
17 = wlr_libinput_get_device_handle(wlr_device);
18
19 enum libinput_config_send_events_mode mode =
20 libinput_device_config_send_events_get_mode(libinput_dev);
21 uint32_t possible =
22 libinput_device_config_send_events_get_modes(libinput_dev);
23
24 switch (mode) {
25 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
26 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
27 if (possible & mode) {
28 break;
29 }
30 // fall through
31 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
32 mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
33 if (possible & mode) {
34 break;
35 }
36 // fall through
37 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
38 default:
39 mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
40 break;
41 }
42
43 ic->send_events = mode;
44}
45
46static void toggle_send_events(struct input_config *ic) {
47 struct sway_input_device *input_device = NULL;
48 wl_list_for_each(input_device, &server.input->devices, link) {
49 if (strcmp(input_device->identifier, ic->identifier) == 0) {
50 toggle_send_events_for_device(ic, input_device);
51 }
52 }
53}
54
55static void toggle_wildcard_send_events() {
56 struct sway_input_device *input_device = NULL;
57 wl_list_for_each(input_device, &server.input->devices, link) {
58 struct input_config *ic = new_input_config(input_device->identifier);
59 if (!ic) {
60 break;
61 }
62 toggle_send_events_for_device(ic, input_device);
63 store_input_config(ic);
64 }
65}
66
8struct cmd_results *input_cmd_events(int argc, char **argv) { 67struct cmd_results *input_cmd_events(int argc, char **argv) {
9 struct cmd_results *error = NULL; 68 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) { 69 if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
@@ -23,9 +82,24 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
23 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) { 82 } else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
24 ic->send_events = 83 ic->send_events =
25 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; 84 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
26 } else { 85 } else if (config->reading) {
27 return cmd_results_new(CMD_INVALID, "events", 86 return cmd_results_new(CMD_INVALID, "events",
28 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'"); 87 "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
88 } else if (strcasecmp(argv[0], "toggle") == 0) {
89 if (strcmp(ic->identifier, "*") == 0) {
90 // Update the device input configs and then reset the wildcard
91 // config send events mode so that is does not override the device
92 // ones. The device ones will be applied when attempting to apply
93 // the wildcard config
94 toggle_wildcard_send_events();
95 ic->send_events = INT_MIN;
96 } else {
97 toggle_send_events(ic);
98 }
99 } else {
100 return cmd_results_new(CMD_INVALID, "events",
101 "Expected 'events <enabled|disabled|disabled_on_external_mouse|"
102 "toggle>'");
29 } 103 }
30 104
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 105 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c
index 1958f23c..d82a1fe1 100644
--- a/sway/commands/input/scroll_button.c
+++ b/sway/commands/input/scroll_button.c
@@ -1,9 +1,7 @@
1#include <string.h> 1#include <libevdev/libevdev.h>
2#include <strings.h>
3#include <errno.h>
4#include "sway/config.h" 2#include "sway/config.h"
5#include "sway/commands.h" 3#include "sway/commands.h"
6#include "sway/input/input-manager.h" 4#include "sway/input/cursor.h"
7 5
8struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { 6struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
9 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
@@ -16,22 +14,26 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) {
16 "No input device defined."); 14 "No input device defined.");
17 } 15 }
18 16
19 errno = 0; 17 if (strcmp(*argv, "disable") == 0) {
20 char *endptr; 18 ic->scroll_button = 0;
21 int scroll_button = strtol(*argv, &endptr, 10); 19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 if (endptr == *argv && scroll_button == 0) {
23 return cmd_results_new(CMD_INVALID, "scroll_button",
24 "Scroll button identifier must be an integer.");
25 } 20 }
26 if (errno == ERANGE) { 21
22 char *message = NULL;
23 uint32_t button = get_mouse_button(*argv, &message);
24 if (message) {
25 error = cmd_results_new(CMD_INVALID, "scroll_button", message);
26 free(message);
27 return error;
28 } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN
29 || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) {
27 return cmd_results_new(CMD_INVALID, "scroll_button", 30 return cmd_results_new(CMD_INVALID, "scroll_button",
28 "Scroll button identifier out of range."); 31 "X11 axis buttons are not supported for scroll_button");
29 } 32 } else if (!button) {
30 if (scroll_button < 0) {
31 return cmd_results_new(CMD_INVALID, "scroll_button", 33 return cmd_results_new(CMD_INVALID, "scroll_button",
32 "Scroll button identifier cannot be negative."); 34 "Unknown button %s", *argv);
33 } 35 }
34 ic->scroll_button = scroll_button; 36 ic->scroll_button = button;
35 37
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 38 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37} 39}
diff --git a/sway/config.c b/sway/config.c
index ea9f23dd..5ca4806c 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -387,6 +387,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
387 memcpy(&config->swaynag_config_errors, 387 memcpy(&config->swaynag_config_errors,
388 &old_config->swaynag_config_errors, 388 &old_config->swaynag_config_errors,
389 sizeof(struct swaynag_instance)); 389 sizeof(struct swaynag_instance));
390
391 input_manager_reset_all_inputs();
390 } 392 }
391 393
392 config->current_config_path = path; 394 config->current_config_path = path;
@@ -571,15 +573,18 @@ bool load_include_configs(const char *path, struct sway_config *config,
571} 573}
572 574
573// get line, with backslash continuation 575// get line, with backslash continuation
574static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file) { 576static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file,
577 int *nlines) {
575 char *next_line = NULL; 578 char *next_line = NULL;
576 size_t next_line_size = 0; 579 size_t next_line_size = 0;
577 ssize_t nread = getline(lineptr, line_size, file); 580 ssize_t nread = getline(lineptr, line_size, file);
581 *nlines = nread == -1 ? 0 : 1;
578 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) { 582 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) {
579 ssize_t next_nread = getline(&next_line, &next_line_size, file); 583 ssize_t next_nread = getline(&next_line, &next_line_size, file);
580 if (next_nread == -1) { 584 if (next_nread == -1) {
581 break; 585 break;
582 } 586 }
587 (*nlines)++;
583 588
584 nread += next_nread - 2; 589 nread += next_nread - 2;
585 if ((ssize_t) *line_size < nread + 1) { 590 if ((ssize_t) *line_size < nread + 1) {
@@ -613,7 +618,9 @@ static int detect_brace(FILE *file) {
613 } 618 }
614 } 619 }
615 free(line); 620 free(line);
616 fseek(file, pos, SEEK_SET); 621 if (ret == 0) {
622 fseek(file, pos, SEEK_SET);
623 }
617 return ret; 624 return ret;
618} 625}
619 626
@@ -661,7 +668,8 @@ bool read_config(FILE *file, struct sway_config *config,
661 ssize_t nread; 668 ssize_t nread;
662 list_t *stack = create_list(); 669 list_t *stack = create_list();
663 size_t read = 0; 670 size_t read = 0;
664 while ((nread = getline_with_cont(&line, &line_size, file)) != -1) { 671 int nlines = 0;
672 while ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) {
665 if (reading_main_config) { 673 if (reading_main_config) {
666 if (read + nread > config_size) { 674 if (read + nread > config_size) {
667 wlr_log(WLR_ERROR, "Config file changed during reading"); 675 wlr_log(WLR_ERROR, "Config file changed during reading");
@@ -677,7 +685,7 @@ bool read_config(FILE *file, struct sway_config *config,
677 line[nread - 1] = '\0'; 685 line[nread - 1] = '\0';
678 } 686 }
679 687
680 line_number++; 688 line_number += nlines;
681 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); 689 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
682 690
683 strip_whitespace(line); 691 strip_whitespace(line);
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 670219f1..701bf051 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -96,7 +96,7 @@ struct bar_config *default_bar_config(void) {
96 bar->pango_markup = false; 96 bar->pango_markup = false;
97 bar->swaybar_command = NULL; 97 bar->swaybar_command = NULL;
98 bar->font = NULL; 98 bar->font = NULL;
99 bar->height = -1; 99 bar->height = 0;
100 bar->workspace_buttons = true; 100 bar->workspace_buttons = true;
101 bar->wrap_scroll = false; 101 bar->wrap_scroll = false;
102 bar->separator_symbol = NULL; 102 bar->separator_symbol = NULL;
@@ -106,6 +106,8 @@ struct bar_config *default_bar_config(void) {
106 bar->verbose = false; 106 bar->verbose = false;
107 bar->pid = 0; 107 bar->pid = 0;
108 bar->modifier = get_modifier_mask_by_name("Mod4"); 108 bar->modifier = get_modifier_mask_by_name("Mod4");
109 bar->status_padding = 1;
110 bar->status_edge_padding = 3;
109 if (!(bar->mode = strdup("dock"))) { 111 if (!(bar->mode = strdup("dock"))) {
110 goto cleanup; 112 goto cleanup;
111 } 113 }
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 79ad7faa..04c9b4f6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -300,7 +300,7 @@ static int scale_length(int length, int offset, float scale) {
300 return round((offset + length) * scale) - round(offset * scale); 300 return round((offset + length) * scale) - round(offset * scale);
301} 301}
302 302
303static void scale_box(struct wlr_box *box, float scale) { 303void scale_box(struct wlr_box *box, float scale) {
304 box->width = scale_length(box->width, box->x, scale); 304 box->width = scale_length(box->width, box->x, scale);
305 box->height = scale_length(box->height, box->y, scale); 305 box->height = scale_length(box->height, box->y, scale);
306 box->x = round(box->x * scale); 306 box->x = round(box->x * scale);
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 6c9fe23c..a38c6a07 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -49,13 +49,6 @@ static int scale_length(int length, int offset, float scale) {
49 return round((offset + length) * scale) - round(offset * scale); 49 return round((offset + length) * scale) - round(offset * scale);
50} 50}
51 51
52static void scale_box(struct wlr_box *box, float scale) {
53 box->width = scale_length(box->width, box->x, scale);
54 box->height = scale_length(box->height, box->y, scale);
55 box->x = round(box->x * scale);
56 box->y = round(box->y * scale);
57}
58
59static void scissor_output(struct wlr_output *wlr_output, 52static void scissor_output(struct wlr_output *wlr_output,
60 pixman_box32_t *rect) { 53 pixman_box32_t *rect) {
61 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); 54 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
@@ -164,7 +157,7 @@ static void render_drag_icons(struct sway_output *output,
164 157
165// _box.x and .y are expected to be layout-local 158// _box.x and .y are expected to be layout-local
166// _box.width and .height are expected to be output-buffer-local 159// _box.width and .height are expected to be output-buffer-local
167static void render_rect(struct wlr_output *wlr_output, 160void render_rect(struct wlr_output *wlr_output,
168 pixman_region32_t *output_damage, const struct wlr_box *_box, 161 pixman_region32_t *output_damage, const struct wlr_box *_box,
169 float color[static 4]) { 162 float color[static 4]) {
170 struct wlr_renderer *renderer = 163 struct wlr_renderer *renderer =
@@ -197,7 +190,7 @@ damage_finish:
197 pixman_region32_fini(&damage); 190 pixman_region32_fini(&damage);
198} 191}
199 192
200static void premultiply_alpha(float color[4], float opacity) { 193void premultiply_alpha(float color[4], float opacity) {
201 color[3] *= opacity; 194 color[3] *= opacity;
202 color[0] *= color[3]; 195 color[0] *= color[3];
203 color[1] *= color[3]; 196 color[1] *= color[3];
@@ -949,21 +942,11 @@ static void render_floating(struct sway_output *soutput,
949 } 942 }
950} 943}
951 944
952static void render_dropzones(struct sway_output *output, 945static void render_seatops(struct sway_output *output,
953 pixman_region32_t *damage) { 946 pixman_region32_t *damage) {
954 struct sway_seat *seat; 947 struct sway_seat *seat;
955 wl_list_for_each(seat, &server.input->seats, link) { 948 wl_list_for_each(seat, &server.input->seats, link) {
956 if (seat->operation == OP_MOVE_TILING && seat->op_target_node 949 seatop_render(seat, output, damage);
957 && node_get_output(seat->op_target_node) == output) {
958 float color[4];
959 memcpy(&color, config->border_colors.focused.indicator,
960 sizeof(float) * 4);
961 premultiply_alpha(color, 0.5);
962 struct wlr_box box;
963 memcpy(&box, &seat->op_drop_box, sizeof(struct wlr_box));
964 scale_box(&box, output->wlr_output->scale);
965 render_rect(output->wlr_output, damage, &box, color);
966 }
967 } 950 }
968} 951}
969 952
@@ -1060,7 +1043,7 @@ void output_render(struct sway_output *output, struct timespec *when,
1060 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 1043 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1061 } 1044 }
1062 1045
1063 render_dropzones(output, damage); 1046 render_seatops(output, damage);
1064 1047
1065 struct sway_seat *seat = input_manager_current_seat(); 1048 struct sway_seat *seat = input_manager_current_seat();
1066 struct sway_container *focus = seat_get_focused_container(seat); 1049 struct sway_container *focus = seat_get_focused_container(seat);
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index f46938e2..1cdd7c6d 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -299,7 +299,7 @@ static void transaction_apply(struct sway_transaction *transaction) {
299 if (root->outputs->length) { 299 if (root->outputs->length) {
300 struct sway_seat *seat; 300 struct sway_seat *seat;
301 wl_list_for_each(seat, &server.input->seats, link) { 301 wl_list_for_each(seat, &server.input->seats, link) {
302 if (seat->operation == OP_NONE) { 302 if (!seat_doing_seatop(seat)) {
303 cursor_rebase(seat->cursor); 303 cursor_rebase(seat->cursor);
304 } 304 }
305 } 305 }
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 801dcee0..f05e156f 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -360,7 +360,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
360 struct wlr_xdg_toplevel_move_event *e = data; 360 struct wlr_xdg_toplevel_move_event *e = data;
361 struct sway_seat *seat = e->seat->seat->data; 361 struct sway_seat *seat = e->seat->seat->data;
362 if (e->serial == seat->last_button_serial) { 362 if (e->serial == seat->last_button_serial) {
363 seat_begin_move_floating(seat, view->container, seat->last_button); 363 seatop_begin_move_floating(seat, view->container, seat->last_button);
364 } 364 }
365} 365}
366 366
@@ -374,7 +374,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
374 struct wlr_xdg_toplevel_resize_event *e = data; 374 struct wlr_xdg_toplevel_resize_event *e = data;
375 struct sway_seat *seat = e->seat->seat->data; 375 struct sway_seat *seat = e->seat->seat->data;
376 if (e->serial == seat->last_button_serial) { 376 if (e->serial == seat->last_button_serial) {
377 seat_begin_resize_floating(seat, view->container, 377 seatop_begin_resize_floating(seat, view->container,
378 seat->last_button, e->edges); 378 seat->last_button, e->edges);
379 } 379 }
380} 380}
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 4bc83b8e..9f6741c8 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -357,7 +357,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
357 struct wlr_xdg_toplevel_v6_move_event *e = data; 357 struct wlr_xdg_toplevel_v6_move_event *e = data;
358 struct sway_seat *seat = e->seat->seat->data; 358 struct sway_seat *seat = e->seat->seat->data;
359 if (e->serial == seat->last_button_serial) { 359 if (e->serial == seat->last_button_serial) {
360 seat_begin_move_floating(seat, view->container, seat->last_button); 360 seatop_begin_move_floating(seat, view->container, seat->last_button);
361 } 361 }
362} 362}
363 363
@@ -371,7 +371,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
371 struct wlr_xdg_toplevel_v6_resize_event *e = data; 371 struct wlr_xdg_toplevel_v6_resize_event *e = data;
372 struct sway_seat *seat = e->seat->seat->data; 372 struct sway_seat *seat = e->seat->seat->data;
373 if (e->serial == seat->last_button_serial) { 373 if (e->serial == seat->last_button_serial) {
374 seat_begin_resize_floating(seat, view->container, 374 seatop_begin_resize_floating(seat, view->container,
375 seat->last_button, e->edges); 375 seat->last_button, e->edges);
376 } 376 }
377} 377}
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 1838ad32..080f6c41 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -470,7 +470,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
470 return; 470 return;
471 } 471 }
472 struct sway_seat *seat = input_manager_current_seat(); 472 struct sway_seat *seat = input_manager_current_seat();
473 seat_begin_move_floating(seat, view->container, seat->last_button); 473 seatop_begin_move_floating(seat, view->container, seat->last_button);
474} 474}
475 475
476static void handle_request_resize(struct wl_listener *listener, void *data) { 476static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -486,7 +486,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
486 } 486 }
487 struct wlr_xwayland_resize_event *e = data; 487 struct wlr_xwayland_resize_event *e = data;
488 struct sway_seat *seat = input_manager_current_seat(); 488 struct sway_seat *seat = input_manager_current_seat();
489 seat_begin_resize_floating(seat, view->container, 489 seatop_begin_resize_floating(seat, view->container,
490 seat->last_button, e->edges); 490 seat->last_button, e->edges);
491} 491}
492 492
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index c1c8b6bf..08222494 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -27,10 +27,6 @@
27#include "sway/tree/workspace.h" 27#include "sway/tree/workspace.h"
28#include "wlr-layer-shell-unstable-v1-protocol.h" 28#include "wlr-layer-shell-unstable-v1-protocol.h"
29 29
30// When doing a tiling drag, this is the thickness of the dropzone
31// when dragging to the edge of a layout container.
32#define DROP_LAYOUT_BORDER 30
33
34static uint32_t get_current_time_msec(void) { 30static uint32_t get_current_time_msec(void) {
35 struct timespec now; 31 struct timespec now;
36 clock_gettime(CLOCK_MONOTONIC, &now); 32 clock_gettime(CLOCK_MONOTONIC, &now);
@@ -59,7 +55,7 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
59 * Returns the node at the cursor's position. If there is a surface at that 55 * Returns the node at the cursor's position. If there is a surface at that
60 * location, it is stored in **surface (it may not be a view). 56 * location, it is stored in **surface (it may not be a view).
61 */ 57 */
62static struct sway_node *node_at_coords( 58struct sway_node *node_at_coords(
63 struct sway_seat *seat, double lx, double ly, 59 struct sway_seat *seat, double lx, double ly,
64 struct wlr_surface **surface, double *sx, double *sy) { 60 struct wlr_surface **surface, double *sx, double *sy) {
65 // check for unmanaged views first 61 // check for unmanaged views first
@@ -226,347 +222,6 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
226 return edge; 222 return edge;
227} 223}
228 224
229static void handle_down_motion(struct sway_seat *seat,
230 struct sway_cursor *cursor, uint32_t time_msec) {
231 struct sway_container *con = seat->op_container;
232 if (seat_is_input_allowed(seat, con->view->surface)) {
233 double moved_x = cursor->cursor->x - seat->op_ref_lx;
234 double moved_y = cursor->cursor->y - seat->op_ref_ly;
235 double sx = seat->op_ref_con_lx + moved_x;
236 double sy = seat->op_ref_con_ly + moved_y;
237 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
238 }
239 seat->op_moved = true;
240}
241
242static void handle_move_floating_motion(struct sway_seat *seat,
243 struct sway_cursor *cursor) {
244 struct sway_container *con = seat->op_container;
245 desktop_damage_whole_container(con);
246 container_floating_translate(con,
247 cursor->cursor->x - cursor->previous.x,
248 cursor->cursor->y - cursor->previous.y);
249 desktop_damage_whole_container(con);
250}
251
252static void resize_box(struct wlr_box *box, enum wlr_edges edge,
253 int thickness) {
254 switch (edge) {
255 case WLR_EDGE_TOP:
256 box->height = thickness;
257 break;
258 case WLR_EDGE_LEFT:
259 box->width = thickness;
260 break;
261 case WLR_EDGE_RIGHT:
262 box->x = box->x + box->width - thickness;
263 box->width = thickness;
264 break;
265 case WLR_EDGE_BOTTOM:
266 box->y = box->y + box->height - thickness;
267 box->height = thickness;
268 break;
269 case WLR_EDGE_NONE:
270 box->x += thickness;
271 box->y += thickness;
272 box->width -= thickness * 2;
273 box->height -= thickness * 2;
274 break;
275 }
276}
277
278static void handle_move_tiling_motion(struct sway_seat *seat,
279 struct sway_cursor *cursor) {
280 struct wlr_surface *surface = NULL;
281 double sx, sy;
282 struct sway_node *node = node_at_coords(seat,
283 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
284 // Damage the old location
285 desktop_damage_box(&seat->op_drop_box);
286
287 if (!node) {
288 // Eg. hovered over a layer surface such as swaybar
289 seat->op_target_node = NULL;
290 seat->op_target_edge = WLR_EDGE_NONE;
291 return;
292 }
293
294 if (node->type == N_WORKSPACE) {
295 // Emtpy workspace
296 seat->op_target_node = node;
297 seat->op_target_edge = WLR_EDGE_NONE;
298 workspace_get_box(node->sway_workspace, &seat->op_drop_box);
299 desktop_damage_box(&seat->op_drop_box);
300 return;
301 }
302
303 // Deny moving within own workspace if this is the only child
304 struct sway_container *con = node->sway_container;
305 if (workspace_num_tiling_views(seat->op_container->workspace) == 1 &&
306 con->workspace == seat->op_container->workspace) {
307 seat->op_target_node = NULL;
308 seat->op_target_edge = WLR_EDGE_NONE;
309 return;
310 }
311
312 // Traverse the ancestors, trying to find a layout container perpendicular
313 // to the edge. Eg. close to the top or bottom of a horiz layout.
314 while (con) {
315 enum wlr_edges edge = WLR_EDGE_NONE;
316 enum sway_container_layout layout = container_parent_layout(con);
317 struct wlr_box parent;
318 con->parent ? container_get_box(con->parent, &parent) :
319 workspace_get_box(con->workspace, &parent);
320 if (layout == L_HORIZ || layout == L_TABBED) {
321 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
322 edge = WLR_EDGE_TOP;
323 } else if (cursor->cursor->y > parent.y + parent.height
324 - DROP_LAYOUT_BORDER) {
325 edge = WLR_EDGE_BOTTOM;
326 }
327 } else if (layout == L_VERT || layout == L_STACKED) {
328 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
329 edge = WLR_EDGE_LEFT;
330 } else if (cursor->cursor->x > parent.x + parent.width
331 - DROP_LAYOUT_BORDER) {
332 edge = WLR_EDGE_RIGHT;
333 }
334 }
335 if (edge) {
336 seat->op_target_node = node_get_parent(&con->node);
337 seat->op_target_edge = edge;
338 node_get_box(seat->op_target_node, &seat->op_drop_box);
339 resize_box(&seat->op_drop_box, edge, DROP_LAYOUT_BORDER);
340 desktop_damage_box(&seat->op_drop_box);
341 return;
342 }
343 con = con->parent;
344 }
345
346 // Use the hovered view - but we must be over the actual surface
347 con = node->sway_container;
348 if (!con->view->surface || node == &seat->op_container->node) {
349 seat->op_target_node = NULL;
350 seat->op_target_edge = WLR_EDGE_NONE;
351 return;
352 }
353
354 // Find the closest edge
355 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
356 size_t closest_dist = INT_MAX;
357 size_t dist;
358 seat->op_target_edge = WLR_EDGE_NONE;
359 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
360 closest_dist = dist;
361 seat->op_target_edge = WLR_EDGE_TOP;
362 }
363 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
364 closest_dist = dist;
365 seat->op_target_edge = WLR_EDGE_LEFT;
366 }
367 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
368 closest_dist = dist;
369 seat->op_target_edge = WLR_EDGE_RIGHT;
370 }
371 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
372 closest_dist = dist;
373 seat->op_target_edge = WLR_EDGE_BOTTOM;
374 }
375
376 if (closest_dist > thickness) {
377 seat->op_target_edge = WLR_EDGE_NONE;
378 }
379
380 seat->op_target_node = node;
381 seat->op_drop_box.x = con->content_x;
382 seat->op_drop_box.y = con->content_y;
383 seat->op_drop_box.width = con->content_width;
384 seat->op_drop_box.height = con->content_height;
385 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness);
386 desktop_damage_box(&seat->op_drop_box);
387}
388
389static void handle_move_tiling_threshold_motion(struct sway_seat *seat,
390 struct sway_cursor *cursor) {
391 double cx = seat->cursor->cursor->x;
392 double cy = seat->cursor->cursor->y;
393 double sx = seat->op_ref_lx;
394 double sy = seat->op_ref_ly;
395
396 // Get the scaled threshold for the output. Even if the operation goes
397 // across multiple outputs of varying scales, just use the scale for the
398 // output that the cursor is currently on for simplicity.
399 struct wlr_output *wlr_output = wlr_output_layout_output_at(
400 root->output_layout, cx, cy);
401 double output_scale = wlr_output ? wlr_output->scale : 1;
402 double threshold = config->tiling_drag_threshold * output_scale;
403 threshold *= threshold;
404
405 // If the threshold has been exceeded, start the actual drag
406 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
407 seat->operation = OP_MOVE_TILING;
408 cursor_set_image(cursor, "grab", NULL);
409 handle_move_tiling_motion(seat, cursor);
410 }
411}
412
413static void calculate_floating_constraints(struct sway_container *con,
414 int *min_width, int *max_width, int *min_height, int *max_height) {
415 if (config->floating_minimum_width == -1) { // no minimum
416 *min_width = 0;
417 } else if (config->floating_minimum_width == 0) { // automatic
418 *min_width = 75;
419 } else {
420 *min_width = config->floating_minimum_width;
421 }
422
423 if (config->floating_minimum_height == -1) { // no minimum
424 *min_height = 0;
425 } else if (config->floating_minimum_height == 0) { // automatic
426 *min_height = 50;
427 } else {
428 *min_height = config->floating_minimum_height;
429 }
430
431 if (config->floating_maximum_width == -1) { // no maximum
432 *max_width = INT_MAX;
433 } else if (config->floating_maximum_width == 0) { // automatic
434 *max_width = con->workspace->width;
435 } else {
436 *max_width = config->floating_maximum_width;
437 }
438
439 if (config->floating_maximum_height == -1) { // no maximum
440 *max_height = INT_MAX;
441 } else if (config->floating_maximum_height == 0) { // automatic
442 *max_height = con->workspace->height;
443 } else {
444 *max_height = config->floating_maximum_height;
445 }
446}
447
448static void handle_resize_floating_motion(struct sway_seat *seat,
449 struct sway_cursor *cursor) {
450 struct sway_container *con = seat->op_container;
451 enum wlr_edges edge = seat->op_resize_edge;
452
453 // The amount the mouse has moved since the start of the resize operation
454 // Positive is down/right
455 double mouse_move_x = cursor->cursor->x - seat->op_ref_lx;
456 double mouse_move_y = cursor->cursor->y - seat->op_ref_ly;
457
458 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
459 mouse_move_x = 0;
460 }
461 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
462 mouse_move_y = 0;
463 }
464
465 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
466 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
467
468 if (seat->op_resize_preserve_ratio) {
469 double x_multiplier = grow_width / seat->op_ref_width;
470 double y_multiplier = grow_height / seat->op_ref_height;
471 double max_multiplier = fmax(x_multiplier, y_multiplier);
472 grow_width = seat->op_ref_width * max_multiplier;
473 grow_height = seat->op_ref_height * max_multiplier;
474 }
475
476 // Determine new width/height, and accommodate for floating min/max values
477 double width = seat->op_ref_width + grow_width;
478 double height = seat->op_ref_height + grow_height;
479 int min_width, max_width, min_height, max_height;
480 calculate_floating_constraints(con, &min_width, &max_width,
481 &min_height, &max_height);
482 width = fmax(min_width, fmin(width, max_width));
483 height = fmax(min_height, fmin(height, max_height));
484
485 // Apply the view's min/max size
486 if (con->view) {
487 double view_min_width, view_max_width, view_min_height, view_max_height;
488 view_get_constraints(con->view, &view_min_width, &view_max_width,
489 &view_min_height, &view_max_height);
490 width = fmax(view_min_width, fmin(width, view_max_width));
491 height = fmax(view_min_height, fmin(height, view_max_height));
492 }
493
494 // Recalculate these, in case we hit a min/max limit
495 grow_width = width - seat->op_ref_width;
496 grow_height = height - seat->op_ref_height;
497
498 // Determine grow x/y values - these are relative to the container's x/y at
499 // the start of the resize operation.
500 double grow_x = 0, grow_y = 0;
501 if (edge & WLR_EDGE_LEFT) {
502 grow_x = -grow_width;
503 } else if (edge & WLR_EDGE_RIGHT) {
504 grow_x = 0;
505 } else {
506 grow_x = -grow_width / 2;
507 }
508 if (edge & WLR_EDGE_TOP) {
509 grow_y = -grow_height;
510 } else if (edge & WLR_EDGE_BOTTOM) {
511 grow_y = 0;
512 } else {
513 grow_y = -grow_height / 2;
514 }
515
516 // Determine the amounts we need to bump everything relative to the current
517 // size.
518 int relative_grow_width = width - con->width;
519 int relative_grow_height = height - con->height;
520 int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x;
521 int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y;
522
523 // Actually resize stuff
524 con->x += relative_grow_x;
525 con->y += relative_grow_y;
526 con->width += relative_grow_width;
527 con->height += relative_grow_height;
528
529 con->content_x += relative_grow_x;
530 con->content_y += relative_grow_y;
531 con->content_width += relative_grow_width;
532 con->content_height += relative_grow_height;
533
534 arrange_container(con);
535}
536
537static void handle_resize_tiling_motion(struct sway_seat *seat,
538 struct sway_cursor *cursor) {
539 int amount_x = 0;
540 int amount_y = 0;
541 int moved_x = cursor->cursor->x - seat->op_ref_lx;
542 int moved_y = cursor->cursor->y - seat->op_ref_ly;
543 enum wlr_edges edge_x = WLR_EDGE_NONE;
544 enum wlr_edges edge_y = WLR_EDGE_NONE;
545 struct sway_container *con = seat->op_container;
546
547 if (seat->op_resize_edge & WLR_EDGE_TOP) {
548 amount_y = (seat->op_ref_height - moved_y) - con->height;
549 edge_y = WLR_EDGE_TOP;
550 } else if (seat->op_resize_edge & WLR_EDGE_BOTTOM) {
551 amount_y = (seat->op_ref_height + moved_y) - con->height;
552 edge_y = WLR_EDGE_BOTTOM;
553 }
554 if (seat->op_resize_edge & WLR_EDGE_LEFT) {
555 amount_x = (seat->op_ref_width - moved_x) - con->width;
556 edge_x = WLR_EDGE_LEFT;
557 } else if (seat->op_resize_edge & WLR_EDGE_RIGHT) {
558 amount_x = (seat->op_ref_width + moved_x) - con->width;
559 edge_x = WLR_EDGE_RIGHT;
560 }
561
562 if (amount_x != 0) {
563 container_resize_tiled(seat->op_container, edge_x, amount_x);
564 }
565 if (amount_y != 0) {
566 container_resize_tiled(seat->op_container, edge_y, amount_y);
567 }
568}
569
570static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, 225static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec,
571 struct sway_node *node, struct wlr_surface *surface, 226 struct sway_node *node, struct wlr_surface *surface,
572 double sx, double sy) { 227 double sx, double sy) {
@@ -669,29 +324,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
669 struct sway_seat *seat = cursor->seat; 324 struct sway_seat *seat = cursor->seat;
670 struct wlr_seat *wlr_seat = seat->wlr_seat; 325 struct wlr_seat *wlr_seat = seat->wlr_seat;
671 326
672 if (seat->operation != OP_NONE) { 327 if (seat_doing_seatop(seat)) {
673 switch (seat->operation) { 328 seatop_motion(seat, time_msec);
674 case OP_DOWN:
675 handle_down_motion(seat, cursor, time_msec);
676 break;
677 case OP_MOVE_FLOATING:
678 handle_move_floating_motion(seat, cursor);
679 break;
680 case OP_MOVE_TILING_THRESHOLD:
681 handle_move_tiling_threshold_motion(seat, cursor);
682 break;
683 case OP_MOVE_TILING:
684 handle_move_tiling_motion(seat, cursor);
685 break;
686 case OP_RESIZE_FLOATING:
687 handle_resize_floating_motion(seat, cursor);
688 break;
689 case OP_RESIZE_TILING:
690 handle_resize_tiling_motion(seat, cursor);
691 break;
692 case OP_NONE:
693 break;
694 }
695 cursor->previous.x = cursor->cursor->x; 329 cursor->previous.x = cursor->cursor->x;
696 cursor->previous.y = cursor->cursor->y; 330 cursor->previous.y = cursor->cursor->y;
697 return; 331 return;
@@ -868,9 +502,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
868 struct sway_seat *seat = cursor->seat; 502 struct sway_seat *seat = cursor->seat;
869 503
870 // Handle existing seat operation 504 // Handle existing seat operation
871 if (cursor->seat->operation != OP_NONE) { 505 if (seat_doing_seatop(seat)) {
872 if (button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { 506 if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) {
873 seat_end_mouse_operation(seat); 507 seatop_finish(seat);
874 seat_pointer_notify_button(seat, time_msec, button, state); 508 seat_pointer_notify_button(seat, time_msec, button, state);
875 } 509 }
876 if (state == WLR_BUTTON_PRESSED) { 510 if (state == WLR_BUTTON_PRESSED) {
@@ -943,7 +577,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
943 if (cont && resize_edge && button == BTN_LEFT && 577 if (cont && resize_edge && button == BTN_LEFT &&
944 state == WLR_BUTTON_PRESSED && !is_floating) { 578 state == WLR_BUTTON_PRESSED && !is_floating) {
945 seat_set_focus_container(seat, cont); 579 seat_set_focus_container(seat, cont);
946 seat_begin_resize_tiling(seat, cont, button, edge); 580 seatop_begin_resize_tiling(seat, cont, button, edge);
947 return; 581 return;
948 } 582 }
949 583
@@ -973,7 +607,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
973 } 607 }
974 cursor_set_image(seat->cursor, image, NULL); 608 cursor_set_image(seat->cursor, image, NULL);
975 seat_set_focus_container(seat, cont); 609 seat_set_focus_container(seat, cont);
976 seat_begin_resize_tiling(seat, cont, button, edge); 610 seatop_begin_resize_tiling(seat, cont, button, edge);
977 return; 611 return;
978 } 612 }
979 } 613 }
@@ -988,7 +622,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
988 cont = cont->parent; 622 cont = cont->parent;
989 } 623 }
990 seat_set_focus_container(seat, cont); 624 seat_set_focus_container(seat, cont);
991 seat_begin_move_floating(seat, cont, button); 625 seatop_begin_move_floating(seat, cont, button);
992 return; 626 return;
993 } 627 }
994 } 628 }
@@ -998,7 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
998 state == WLR_BUTTON_PRESSED) { 632 state == WLR_BUTTON_PRESSED) {
999 // Via border 633 // Via border
1000 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 634 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
1001 seat_begin_resize_floating(seat, cont, button, resize_edge); 635 seatop_begin_resize_floating(seat, cont, button, resize_edge);
1002 return; 636 return;
1003 } 637 }
1004 638
@@ -1015,7 +649,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
1015 WLR_EDGE_RIGHT : WLR_EDGE_LEFT; 649 WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
1016 edge |= cursor->cursor->y > floater->y + floater->height / 2 ? 650 edge |= cursor->cursor->y > floater->y + floater->height / 2 ?
1017 WLR_EDGE_BOTTOM : WLR_EDGE_TOP; 651 WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
1018 seat_begin_resize_floating(seat, floater, button, edge); 652 seatop_begin_resize_floating(seat, floater, button, edge);
1019 return; 653 return;
1020 } 654 }
1021 } 655 }
@@ -1035,9 +669,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
1035 669
1036 // If moving a container by it's title bar, use a threshold for the drag 670 // If moving a container by it's title bar, use a threshold for the drag
1037 if (!mod_pressed && config->tiling_drag_threshold > 0) { 671 if (!mod_pressed && config->tiling_drag_threshold > 0) {
1038 seat_begin_move_tiling_threshold(seat, cont, button); 672 seatop_begin_move_tiling_threshold(seat, cont, button);
1039 } else { 673 } else {
1040 seat_begin_move_tiling(seat, cont, button); 674 seatop_begin_move_tiling(seat, cont, button);
1041 } 675 }
1042 return; 676 return;
1043 } 677 }
@@ -1046,7 +680,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
1046 if (surface && cont && state == WLR_BUTTON_PRESSED) { 680 if (surface && cont && state == WLR_BUTTON_PRESSED) {
1047 seat_set_focus_container(seat, cont); 681 seat_set_focus_container(seat, cont);
1048 seat_pointer_notify_button(seat, time_msec, button, state); 682 seat_pointer_notify_button(seat, time_msec, button, state);
1049 seat_begin_down(seat, cont, button, sx, sy); 683 seatop_begin_down(seat, cont, button, sx, sy);
1050 return; 684 return;
1051 } 685 }
1052 686
@@ -1353,7 +987,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
1353 void *data) { 987 void *data) {
1354 struct sway_cursor *cursor = 988 struct sway_cursor *cursor =
1355 wl_container_of(listener, cursor, request_set_cursor); 989 wl_container_of(listener, cursor, request_set_cursor);
1356 if (cursor->seat->operation != OP_NONE) { 990 if (seat_doing_seatop(cursor->seat)) {
1357 return; 991 return;
1358 } 992 }
1359 struct wlr_seat_pointer_request_set_cursor_event *event = data; 993 struct wlr_seat_pointer_request_set_cursor_event *event = data;
@@ -1596,7 +1230,7 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
1596 1230
1597uint32_t get_mouse_button(const char *name, char **error) { 1231uint32_t get_mouse_button(const char *name, char **error) {
1598 uint32_t button = get_mouse_bindsym(name, error); 1232 uint32_t button = get_mouse_bindsym(name, error);
1599 if (!button && !error) { 1233 if (!button && !*error) {
1600 button = get_mouse_bindcode(name, error); 1234 button = get_mouse_bindcode(name, error);
1601 } 1235 }
1602 return button; 1236 return button;
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 04e14355..d90803f6 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -129,6 +129,24 @@ static void input_manager_libinput_config_keyboard(
129 } 129 }
130} 130}
131 131
132static void input_manager_libinput_reset_keyboard(
133 struct sway_input_device *input_device) {
134 struct wlr_input_device *wlr_device = input_device->wlr_device;
135 struct libinput_device *libinput_device;
136
137 if (!wlr_input_device_is_libinput(wlr_device)) {
138 return;
139 }
140
141 libinput_device = wlr_libinput_get_device_handle(wlr_device);
142
143 uint32_t send_events =
144 libinput_device_config_send_events_get_default_mode(libinput_device);
145 wlr_log(WLR_DEBUG, "libinput_reset_keyboard(%s) send_events_set_mode(%d)",
146 input_device->identifier, send_events);
147 libinput_device_config_send_events_set_mode(libinput_device, send_events);
148}
149
132static void input_manager_libinput_config_touch( 150static void input_manager_libinput_config_touch(
133 struct sway_input_device *input_device) { 151 struct sway_input_device *input_device) {
134 struct wlr_input_device *wlr_device = input_device->wlr_device; 152 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -151,6 +169,24 @@ static void input_manager_libinput_config_touch(
151 } 169 }
152} 170}
153 171
172static void input_manager_libinput_reset_touch(
173 struct sway_input_device *input_device) {
174 struct wlr_input_device *wlr_device = input_device->wlr_device;
175 struct libinput_device *libinput_device;
176
177 if (!wlr_input_device_is_libinput(wlr_device)) {
178 return;
179 }
180
181 libinput_device = wlr_libinput_get_device_handle(wlr_device);
182
183 uint32_t send_events =
184 libinput_device_config_send_events_get_default_mode(libinput_device);
185 wlr_log(WLR_DEBUG, "libinput_reset_touch(%s) send_events_set_mode(%d)",
186 input_device->identifier, send_events);
187 libinput_device_config_send_events_set_mode(libinput_device, send_events);
188}
189
154static void input_manager_libinput_config_pointer( 190static void input_manager_libinput_config_pointer(
155 struct sway_input_device *input_device) { 191 struct sway_input_device *input_device) {
156 struct wlr_input_device *wlr_device = input_device->wlr_device; 192 struct wlr_input_device *wlr_device = input_device->wlr_device;
@@ -180,14 +216,14 @@ static void input_manager_libinput_config_pointer(
180 if (ic->drag != INT_MIN) { 216 if (ic->drag != INT_MIN) {
181 wlr_log(WLR_DEBUG, 217 wlr_log(WLR_DEBUG,
182 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)", 218 "libinput_config_pointer(%s) tap_set_drag_enabled(%d)",
183 ic->identifier, ic->click_method); 219 ic->identifier, ic->drag);
184 libinput_device_config_tap_set_drag_enabled(libinput_device, 220 libinput_device_config_tap_set_drag_enabled(libinput_device,
185 ic->drag); 221 ic->drag);
186 } 222 }
187 if (ic->drag_lock != INT_MIN) { 223 if (ic->drag_lock != INT_MIN) {
188 wlr_log(WLR_DEBUG, 224 wlr_log(WLR_DEBUG,
189 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)", 225 "libinput_config_pointer(%s) tap_set_drag_lock_enabled(%d)",
190 ic->identifier, ic->click_method); 226 ic->identifier, ic->drag_lock);
191 libinput_device_config_tap_set_drag_lock_enabled(libinput_device, 227 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
192 ic->drag_lock); 228 ic->drag_lock);
193 } 229 }
@@ -248,12 +284,118 @@ static void input_manager_libinput_config_pointer(
248 } 284 }
249 if (ic->tap_button_map != INT_MIN) { 285 if (ic->tap_button_map != INT_MIN) {
250 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)", 286 wlr_log(WLR_DEBUG, "libinput_config_pointer(%s) tap_set_button_map(%d)",
251 ic->identifier, ic->tap); 287 ic->identifier, ic->tap_button_map);
252 libinput_device_config_tap_set_button_map(libinput_device, 288 libinput_device_config_tap_set_button_map(libinput_device,
253 ic->tap_button_map); 289 ic->tap_button_map);
254 } 290 }
255} 291}
256 292
293static void input_manager_libinput_reset_pointer(
294 struct sway_input_device *input_device) {
295 struct wlr_input_device *wlr_device = input_device->wlr_device;
296
297 if (!wlr_input_device_is_libinput(wlr_device)) {
298 return;
299 }
300
301 struct libinput_device *libinput_device =
302 wlr_libinput_get_device_handle(wlr_device);
303
304 enum libinput_config_accel_profile accel_profile =
305 libinput_device_config_accel_get_default_profile(libinput_device);
306 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_profile(%d)",
307 input_device->identifier, accel_profile);
308 libinput_device_config_accel_set_profile(libinput_device, accel_profile);
309
310 enum libinput_config_click_method click_method =
311 libinput_device_config_click_get_default_method(libinput_device);
312 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) click_set_method(%d)",
313 input_device->identifier, click_method);
314 libinput_device_config_click_set_method(libinput_device, click_method);
315
316 enum libinput_config_drag_state drag =
317 libinput_device_config_tap_get_default_drag_enabled(libinput_device);
318 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_drag_enabled(%d)",
319 input_device->identifier, drag);
320 libinput_device_config_tap_set_drag_enabled(libinput_device, drag);
321
322 enum libinput_config_drag_lock_state drag_lock =
323 libinput_device_config_tap_get_default_drag_lock_enabled(
324 libinput_device);
325 wlr_log(WLR_DEBUG,
326 "libinput_reset_pointer(%s) tap_set_drag_lock_enabled(%d)",
327 input_device->identifier, drag_lock);
328 libinput_device_config_tap_set_drag_lock_enabled(libinput_device,
329 drag_lock);
330
331 enum libinput_config_dwt_state dwt =
332 libinput_device_config_dwt_get_default_enabled(libinput_device);
333 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) dwt_set_enabled(%d)",
334 input_device->identifier, dwt);
335 libinput_device_config_dwt_set_enabled(libinput_device, dwt);
336
337 int left_handed =
338 libinput_device_config_left_handed_get_default(libinput_device);
339 wlr_log(WLR_DEBUG,
340 "libinput_reset_pointer(%s) left_handed_set_enabled(%d)",
341 input_device->identifier, left_handed);
342 libinput_device_config_left_handed_set(libinput_device, left_handed);
343
344 enum libinput_config_middle_emulation_state middle_emulation =
345 libinput_device_config_middle_emulation_get_default_enabled(
346 libinput_device);
347 wlr_log(WLR_DEBUG,
348 "libinput_reset_pointer(%s) middle_emulation_set_enabled(%d)",
349 input_device->identifier, middle_emulation);
350 libinput_device_config_middle_emulation_set_enabled(libinput_device,
351 middle_emulation);
352
353 int natural_scroll =
354 libinput_device_config_scroll_get_default_natural_scroll_enabled(
355 libinput_device);
356 wlr_log(WLR_DEBUG,
357 "libinput_reset_pointer(%s) natural_scroll_set_enabled(%d)",
358 input_device->identifier, natural_scroll);
359 libinput_device_config_scroll_set_natural_scroll_enabled(
360 libinput_device, natural_scroll);
361
362 double pointer_accel =
363 libinput_device_config_accel_get_default_speed(libinput_device);
364 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) accel_set_speed(%f)",
365 input_device->identifier, pointer_accel);
366 libinput_device_config_accel_set_speed(libinput_device, pointer_accel);
367
368 uint32_t scroll_button =
369 libinput_device_config_scroll_get_default_button(libinput_device);
370 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_button(%d)",
371 input_device->identifier, scroll_button);
372 libinput_device_config_scroll_set_button(libinput_device, scroll_button);
373
374 enum libinput_config_scroll_method scroll_method =
375 libinput_device_config_scroll_get_default_method(libinput_device);
376 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) scroll_set_method(%d)",
377 input_device->identifier, scroll_method);
378 libinput_device_config_scroll_set_method(libinput_device, scroll_method);
379
380 uint32_t send_events =
381 libinput_device_config_send_events_get_default_mode(libinput_device);
382 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) send_events_set_mode(%d)",
383 input_device->identifier, send_events);
384 libinput_device_config_send_events_set_mode(libinput_device, send_events);
385
386 enum libinput_config_tap_state tap =
387 libinput_device_config_tap_get_default_enabled(libinput_device);
388 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_enabled(%d)",
389 input_device->identifier, tap);
390 libinput_device_config_tap_set_enabled(libinput_device, tap);
391
392 enum libinput_config_tap_button_map tap_button_map =
393 libinput_device_config_tap_get_button_map(libinput_device);
394 wlr_log(WLR_DEBUG, "libinput_reset_pointer(%s) tap_set_button_map(%d)",
395 input_device->identifier, tap_button_map);
396 libinput_device_config_tap_set_button_map(libinput_device, tap_button_map);
397}
398
257static void handle_device_destroy(struct wl_listener *listener, void *data) { 399static void handle_device_destroy(struct wl_listener *listener, void *data) {
258 struct wlr_input_device *device = data; 400 struct wlr_input_device *device = data;
259 401
@@ -466,6 +608,30 @@ void input_manager_apply_input_config(struct input_config *input_config) {
466 } 608 }
467} 609}
468 610
611void input_manager_reset_input(struct sway_input_device *input_device) {
612 if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER ||
613 input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
614 input_manager_libinput_reset_pointer(input_device);
615 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
616 input_manager_libinput_reset_keyboard(input_device);
617 } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) {
618 input_manager_libinput_reset_touch(input_device);
619 }
620
621 struct sway_seat *seat = NULL;
622 wl_list_for_each(seat, &server.input->seats, link) {
623 seat_reset_device(seat, input_device);
624 }
625}
626
627void input_manager_reset_all_inputs() {
628 struct sway_input_device *input_device = NULL;
629 wl_list_for_each(input_device, &server.input->devices, link) {
630 input_manager_reset_input(input_device);
631 }
632}
633
634
469void input_manager_apply_seat_config(struct seat_config *seat_config) { 635void input_manager_apply_seat_config(struct seat_config *seat_config) {
470 wlr_log(WLR_DEBUG, "applying seat config for seat %s", seat_config->name); 636 wlr_log(WLR_DEBUG, "applying seat config for seat %s", seat_config->name);
471 if (strcmp(seat_config->name, "*") == 0) { 637 if (strcmp(seat_config->name, "*") == 0) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index a8df5b99..a63999b6 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -308,7 +308,7 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
308 wl_list_insert(&root->drag_icons, &icon->link); 308 wl_list_insert(&root->drag_icons, &icon->link);
309 309
310 drag_icon_update_position(icon); 310 drag_icon_update_position(icon);
311 seat_end_mouse_operation(seat); 311 seatop_abort(seat);
312} 312}
313 313
314static void collect_focus_iter(struct sway_node *node, void *data) { 314static void collect_focus_iter(struct sway_node *node, void *data) {
@@ -405,6 +405,14 @@ static void seat_update_capabilities(struct sway_seat *seat) {
405 } 405 }
406} 406}
407 407
408static void seat_reset_input_config(struct sway_seat *seat,
409 struct sway_seat_device *sway_device) {
410 wlr_log(WLR_DEBUG, "Resetting output mapping for input device %s",
411 sway_device->input_device->identifier);
412 wlr_cursor_map_input_to_output(seat->cursor->cursor,
413 sway_device->input_device->wlr_device, NULL);
414}
415
408static void seat_apply_input_config(struct sway_seat *seat, 416static void seat_apply_input_config(struct sway_seat *seat,
409 struct sway_seat_device *sway_device) { 417 struct sway_seat_device *sway_device) {
410 const char *mapped_to_output = NULL; 418 const char *mapped_to_output = NULL;
@@ -522,6 +530,35 @@ void seat_configure_device(struct sway_seat *seat,
522 } 530 }
523} 531}
524 532
533void seat_reset_device(struct sway_seat *seat,
534 struct sway_input_device *input_device) {
535 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
536 if (!seat_device) {
537 return;
538 }
539
540 switch (input_device->wlr_device->type) {
541 case WLR_INPUT_DEVICE_POINTER:
542 seat_reset_input_config(seat, seat_device);
543 break;
544 case WLR_INPUT_DEVICE_KEYBOARD:
545 sway_keyboard_configure(seat_device->keyboard);
546 break;
547 case WLR_INPUT_DEVICE_TOUCH:
548 seat_reset_input_config(seat, seat_device);
549 break;
550 case WLR_INPUT_DEVICE_TABLET_TOOL:
551 seat_reset_input_config(seat, seat_device);
552 break;
553 case WLR_INPUT_DEVICE_TABLET_PAD:
554 wlr_log(WLR_DEBUG, "TODO: reset tablet pad");
555 break;
556 case WLR_INPUT_DEVICE_SWITCH:
557 wlr_log(WLR_DEBUG, "TODO: reset switch device");
558 break;
559 }
560}
561
525void seat_add_device(struct sway_seat *seat, 562void seat_add_device(struct sway_seat *seat,
526 struct sway_input_device *input_device) { 563 struct sway_input_device *input_device) {
527 if (seat_get_device(seat, input_device)) { 564 if (seat_get_device(seat, input_device)) {
@@ -625,18 +662,6 @@ static int handle_urgent_timeout(void *data) {
625 return 0; 662 return 0;
626} 663}
627 664
628static void container_raise_floating(struct sway_container *con) {
629 // Bring container to front by putting it at the end of the floating list.
630 struct sway_container *floater = con;
631 while (floater->parent) {
632 floater = floater->parent;
633 }
634 if (container_is_floating(floater)) {
635 list_move_to_end(floater->workspace->floating, floater);
636 node_set_dirty(&floater->workspace->node);
637 }
638}
639
640static void set_workspace(struct sway_seat *seat, 665static void set_workspace(struct sway_seat *seat,
641 struct sway_workspace *new_ws) { 666 struct sway_workspace *new_ws) {
642 if (seat->workspace == new_ws) { 667 if (seat->workspace == new_ws) {
@@ -1025,187 +1050,6 @@ struct seat_config *seat_get_config_by_name(const char *name) {
1025 return NULL; 1050 return NULL;
1026} 1051}
1027 1052
1028void seat_begin_down(struct sway_seat *seat, struct sway_container *con,
1029 uint32_t button, double sx, double sy) {
1030 seat->operation = OP_DOWN;
1031 seat->op_container = con;
1032 seat->op_button = button;
1033 seat->op_ref_lx = seat->cursor->cursor->x;
1034 seat->op_ref_ly = seat->cursor->cursor->y;
1035 seat->op_ref_con_lx = sx;
1036 seat->op_ref_con_ly = sy;
1037 seat->op_moved = false;
1038
1039 container_raise_floating(con);
1040}
1041
1042void seat_begin_move_floating(struct sway_seat *seat,
1043 struct sway_container *con, uint32_t button) {
1044 if (!seat->cursor) {
1045 wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device");
1046 return;
1047 }
1048 seat->operation = OP_MOVE_FLOATING;
1049 seat->op_container = con;
1050 seat->op_button = button;
1051
1052 container_raise_floating(con);
1053
1054 cursor_set_image(seat->cursor, "grab", NULL);
1055}
1056
1057void seat_begin_move_tiling_threshold(struct sway_seat *seat,
1058 struct sway_container *con, uint32_t button) {
1059 seat->operation = OP_MOVE_TILING_THRESHOLD;
1060 seat->op_container = con;
1061 seat->op_button = button;
1062 seat->op_target_node = NULL;
1063 seat->op_target_edge = 0;
1064 seat->op_ref_lx = seat->cursor->cursor->x;
1065 seat->op_ref_ly = seat->cursor->cursor->y;
1066}
1067
1068void seat_begin_move_tiling(struct sway_seat *seat,
1069 struct sway_container *con, uint32_t button) {
1070 seat->operation = OP_MOVE_TILING;
1071 seat->op_container = con;
1072 seat->op_button = button;
1073 seat->op_target_node = NULL;
1074 seat->op_target_edge = 0;
1075 cursor_set_image(seat->cursor, "grab", NULL);
1076}
1077
1078void seat_begin_resize_floating(struct sway_seat *seat,
1079 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1080 if (!seat->cursor) {
1081 wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device");
1082 return;
1083 }
1084 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
1085 seat->operation = OP_RESIZE_FLOATING;
1086 seat->op_container = con;
1087 seat->op_resize_preserve_ratio = keyboard &&
1088 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
1089 seat->op_resize_edge = edge == WLR_EDGE_NONE ?
1090 WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
1091 seat->op_button = button;
1092 seat->op_ref_lx = seat->cursor->cursor->x;
1093 seat->op_ref_ly = seat->cursor->cursor->y;
1094 seat->op_ref_con_lx = con->x;
1095 seat->op_ref_con_ly = con->y;
1096 seat->op_ref_width = con->width;
1097 seat->op_ref_height = con->height;
1098
1099 container_raise_floating(con);
1100
1101 const char *image = edge == WLR_EDGE_NONE ?
1102 "se-resize" : wlr_xcursor_get_resize_name(edge);
1103 cursor_set_image(seat->cursor, image, NULL);
1104}
1105
1106void seat_begin_resize_tiling(struct sway_seat *seat,
1107 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
1108 seat->operation = OP_RESIZE_TILING;
1109 seat->op_container = con;
1110 seat->op_resize_edge = edge;
1111 seat->op_button = button;
1112 seat->op_ref_lx = seat->cursor->cursor->x;
1113 seat->op_ref_ly = seat->cursor->cursor->y;
1114 seat->op_ref_con_lx = con->x;
1115 seat->op_ref_con_ly = con->y;
1116 seat->op_ref_width = con->width;
1117 seat->op_ref_height = con->height;
1118}
1119
1120static bool is_parallel(enum sway_container_layout layout,
1121 enum wlr_edges edge) {
1122 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
1123 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
1124 return layout_is_horiz == edge_is_horiz;
1125}
1126
1127static void seat_end_move_tiling(struct sway_seat *seat) {
1128 struct sway_container *con = seat->op_container;
1129 struct sway_container *old_parent = con->parent;
1130 struct sway_workspace *old_ws = con->workspace;
1131 struct sway_node *target_node = seat->op_target_node;
1132 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
1133 target_node->sway_workspace : target_node->sway_container->workspace;
1134 enum wlr_edges edge = seat->op_target_edge;
1135 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
1136
1137 container_detach(con);
1138
1139 // Moving container into empty workspace
1140 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
1141 workspace_add_tiling(new_ws, con);
1142 } else if (target_node->type == N_CONTAINER) {
1143 // Moving container before/after another
1144 struct sway_container *target = target_node->sway_container;
1145 enum sway_container_layout layout = container_parent_layout(target);
1146 if (edge && !is_parallel(layout, edge)) {
1147 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1148 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1149 container_split(target, new_layout);
1150 }
1151 container_add_sibling(target, con, after);
1152 } else {
1153 // Target is a workspace which requires splitting
1154 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
1155 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
1156 workspace_split(new_ws, new_layout);
1157 workspace_insert_tiling(new_ws, con, after);
1158 }
1159
1160 if (old_parent) {
1161 container_reap_empty(old_parent);
1162 }
1163
1164 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
1165 // I don't think there's any other way to make it consistent without
1166 // changing how we auto-size containers.
1167 list_t *siblings = container_get_siblings(con);
1168 if (siblings->length > 1) {
1169 int index = list_find(siblings, con);
1170 struct sway_container *sibling = index == 0 ?
1171 siblings->items[1] : siblings->items[index - 1];
1172 con->width = sibling->width;
1173 con->height = sibling->height;
1174 }
1175
1176 arrange_workspace(old_ws);
1177 if (new_ws != old_ws) {
1178 arrange_workspace(new_ws);
1179 }
1180}
1181
1182void seat_end_mouse_operation(struct sway_seat *seat) {
1183 enum sway_seat_operation operation = seat->operation;
1184 if (seat->operation == OP_MOVE_FLOATING) {
1185 // We "move" the container to its own location so it discovers its
1186 // output again.
1187 struct sway_container *con = seat->op_container;
1188 container_floating_move_to(con, con->x, con->y);
1189 } else if (seat->operation == OP_MOVE_TILING && seat->op_target_node) {
1190 seat_end_move_tiling(seat);
1191 }
1192 seat->operation = OP_NONE;
1193 seat->op_container = NULL;
1194 if (operation == OP_DOWN) {
1195 // Set the cursor's previous coords to the x/y at the start of the
1196 // operation, so the container change will be detected if using
1197 // focus_follows_mouse and the cursor moved off the original container
1198 // during the operation.
1199 seat->cursor->previous.x = seat->op_ref_lx;
1200 seat->cursor->previous.y = seat->op_ref_ly;
1201 if (seat->op_moved) {
1202 cursor_send_pointer_motion(seat->cursor, 0);
1203 }
1204 } else {
1205 cursor_set_image(seat->cursor, "left_ptr", NULL);
1206 }
1207}
1208
1209void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, 1053void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,
1210 uint32_t button, enum wlr_button_state state) { 1054 uint32_t button, enum wlr_button_state state) {
1211 seat->last_button = button; 1055 seat->last_button = button;
@@ -1238,3 +1082,44 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) {
1238 wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); 1082 wl_event_source_timer_update(seat->cursor->hide_source, cursor_get_timeout(seat->cursor));
1239 } 1083 }
1240} 1084}
1085
1086bool seat_doing_seatop(struct sway_seat *seat) {
1087 return seat->seatop_impl != NULL;
1088}
1089
1090void seatop_unref(struct sway_seat *seat, struct sway_container *con) {
1091 if (seat->seatop_impl && seat->seatop_impl->unref) {
1092 seat->seatop_impl->unref(seat, con);
1093 }
1094}
1095
1096void seatop_motion(struct sway_seat *seat, uint32_t time_msec) {
1097 if (seat->seatop_impl && seat->seatop_impl->motion) {
1098 seat->seatop_impl->motion(seat, time_msec);
1099 }
1100}
1101
1102void seatop_finish(struct sway_seat *seat) {
1103 if (seat->seatop_impl && seat->seatop_impl->finish) {
1104 seat->seatop_impl->finish(seat);
1105 }
1106 free(seat->seatop_data);
1107 seat->seatop_data = NULL;
1108 seat->seatop_impl = NULL;
1109}
1110
1111void seatop_abort(struct sway_seat *seat) {
1112 if (seat->seatop_impl && seat->seatop_impl->abort) {
1113 seat->seatop_impl->abort(seat);
1114 }
1115 free(seat->seatop_data);
1116 seat->seatop_data = NULL;
1117 seat->seatop_impl = NULL;
1118}
1119
1120void seatop_render(struct sway_seat *seat, struct sway_output *output,
1121 pixman_region32_t *damage) {
1122 if (seat->seatop_impl && seat->seatop_impl->render) {
1123 seat->seatop_impl->render(seat, output, damage);
1124 }
1125}
diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c
new file mode 100644
index 00000000..ad11c5ca
--- /dev/null
+++ b/sway/input/seatop_down.c
@@ -0,0 +1,77 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/input/cursor.h"
4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6
7struct seatop_down_event {
8 struct sway_container *con;
9 double ref_lx, ref_ly; // cursor's x/y at start of op
10 double ref_con_lx, ref_con_ly; // container's x/y at start of op
11 bool moved;
12};
13
14static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
15 struct seatop_down_event *e = seat->seatop_data;
16 struct sway_container *con = e->con;
17 if (seat_is_input_allowed(seat, con->view->surface)) {
18 double moved_x = seat->cursor->cursor->x - e->ref_lx;
19 double moved_y = seat->cursor->cursor->y - e->ref_ly;
20 double sx = e->ref_con_lx + moved_x;
21 double sy = e->ref_con_ly + moved_y;
22 wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
23 }
24 e->moved = true;
25}
26
27static void handle_finish(struct sway_seat *seat) {
28 struct seatop_down_event *e = seat->seatop_data;
29 // Set the cursor's previous coords to the x/y at the start of the
30 // operation, so the container change will be detected if using
31 // focus_follows_mouse and the cursor moved off the original container
32 // during the operation.
33 seat->cursor->previous.x = e->ref_lx;
34 seat->cursor->previous.y = e->ref_ly;
35 if (e->moved) {
36 cursor_send_pointer_motion(seat->cursor, 0);
37 }
38}
39
40static void handle_abort(struct sway_seat *seat) {
41 cursor_set_image(seat->cursor, "left_ptr", NULL);
42}
43
44static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
45 struct seatop_down_event *e = seat->seatop_data;
46 if (e->con == con) {
47 seatop_abort(seat);
48 }
49}
50
51static const struct sway_seatop_impl seatop_impl = {
52 .motion = handle_motion,
53 .finish = handle_finish,
54 .abort = handle_abort,
55 .unref = handle_unref,
56};
57
58void seatop_begin_down(struct sway_seat *seat,
59 struct sway_container *con, uint32_t button, int sx, int sy) {
60 seatop_abort(seat);
61
62 struct seatop_down_event *e =
63 calloc(1, sizeof(struct seatop_down_event));
64 if (!e) {
65 return;
66 }
67 e->con = con;
68 e->ref_lx = seat->cursor->cursor->x;
69 e->ref_ly = seat->cursor->cursor->y;
70 e->ref_con_lx = sx;
71 e->ref_con_ly = sy;
72 e->moved = false;
73
74 seat->seatop_impl = &seatop_impl;
75 seat->seatop_data = e;
76 seat->seatop_button = button;
77}
diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c
new file mode 100644
index 00000000..08e3a5a4
--- /dev/null
+++ b/sway/input/seatop_move_floating.c
@@ -0,0 +1,65 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/desktop.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_move_floating_event {
8 struct sway_container *con;
9};
10
11static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
12 struct seatop_move_floating_event *e = seat->seatop_data;
13 desktop_damage_whole_container(e->con);
14 container_floating_translate(e->con,
15 seat->cursor->cursor->x - seat->cursor->previous.x,
16 seat->cursor->cursor->y - seat->cursor->previous.y);
17 desktop_damage_whole_container(e->con);
18}
19
20static void handle_finish(struct sway_seat *seat) {
21 struct seatop_move_floating_event *e = seat->seatop_data;
22
23 // We "move" the container to its own location
24 // so it discovers its output again.
25 container_floating_move_to(e->con, e->con->x, e->con->y);
26 cursor_set_image(seat->cursor, "left_ptr", NULL);
27}
28
29static void handle_abort(struct sway_seat *seat) {
30 cursor_set_image(seat->cursor, "left_ptr", NULL);
31}
32
33static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
34 struct seatop_move_floating_event *e = seat->seatop_data;
35 if (e->con == con) {
36 seatop_abort(seat);
37 }
38}
39
40static const struct sway_seatop_impl seatop_impl = {
41 .motion = handle_motion,
42 .finish = handle_finish,
43 .abort = handle_abort,
44 .unref = handle_unref,
45};
46
47void seatop_begin_move_floating(struct sway_seat *seat,
48 struct sway_container *con, uint32_t button) {
49 seatop_abort(seat);
50
51 struct seatop_move_floating_event *e =
52 calloc(1, sizeof(struct seatop_move_floating_event));
53 if (!e) {
54 return;
55 }
56 e->con = con;
57
58 seat->seatop_impl = &seatop_impl;
59 seat->seatop_data = e;
60 seat->seatop_button = button;
61
62 container_raise_floating(con);
63
64 cursor_set_image(seat->cursor, "grab", NULL);
65}
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
new file mode 100644
index 00000000..8b541f80
--- /dev/null
+++ b/sway/input/seatop_move_tiling.c
@@ -0,0 +1,335 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h>
5#include "sway/desktop.h"
6#include "sway/input/cursor.h"
7#include "sway/input/seat.h"
8#include "sway/output.h"
9#include "sway/tree/arrange.h"
10#include "sway/tree/node.h"
11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
13
14// Thickness of the dropzone when dragging to the edge of a layout container
15#define DROP_LAYOUT_BORDER 30
16
17struct seatop_move_tiling_event {
18 struct sway_container *con;
19 struct sway_node *target_node;
20 enum wlr_edges target_edge;
21 struct wlr_box drop_box;
22 double ref_lx, ref_ly; // cursor's x/y at start of op
23 bool threshold_reached;
24};
25
26static void handle_render(struct sway_seat *seat,
27 struct sway_output *output, pixman_region32_t *damage) {
28 struct seatop_move_tiling_event *e = seat->seatop_data;
29 if (!e->threshold_reached) {
30 return;
31 }
32 if (e->target_node && node_get_output(e->target_node) == output) {
33 float color[4];
34 memcpy(&color, config->border_colors.focused.indicator,
35 sizeof(float) * 4);
36 premultiply_alpha(color, 0.5);
37 struct wlr_box box;
38 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
39 scale_box(&box, output->wlr_output->scale);
40 render_rect(output->wlr_output, damage, &box, color);
41 }
42}
43
44static void handle_motion_prethreshold(struct sway_seat *seat) {
45 struct seatop_move_tiling_event *e = seat->seatop_data;
46 double cx = seat->cursor->cursor->x;
47 double cy = seat->cursor->cursor->y;
48 double sx = e->ref_lx;
49 double sy = e->ref_ly;
50
51 // Get the scaled threshold for the output. Even if the operation goes
52 // across multiple outputs of varying scales, just use the scale for the
53 // output that the cursor is currently on for simplicity.
54 struct wlr_output *wlr_output = wlr_output_layout_output_at(
55 root->output_layout, cx, cy);
56 double output_scale = wlr_output ? wlr_output->scale : 1;
57 double threshold = config->tiling_drag_threshold * output_scale;
58 threshold *= threshold;
59
60 // If the threshold has been exceeded, start the actual drag
61 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
62 e->threshold_reached = true;
63 cursor_set_image(seat->cursor, "grab", NULL);
64 }
65}
66
67static void resize_box(struct wlr_box *box, enum wlr_edges edge,
68 int thickness) {
69 switch (edge) {
70 case WLR_EDGE_TOP:
71 box->height = thickness;
72 break;
73 case WLR_EDGE_LEFT:
74 box->width = thickness;
75 break;
76 case WLR_EDGE_RIGHT:
77 box->x = box->x + box->width - thickness;
78 box->width = thickness;
79 break;
80 case WLR_EDGE_BOTTOM:
81 box->y = box->y + box->height - thickness;
82 box->height = thickness;
83 break;
84 case WLR_EDGE_NONE:
85 box->x += thickness;
86 box->y += thickness;
87 box->width -= thickness * 2;
88 box->height -= thickness * 2;
89 break;
90 }
91}
92
93static void handle_motion_postthreshold(struct sway_seat *seat) {
94 struct seatop_move_tiling_event *e = seat->seatop_data;
95 struct wlr_surface *surface = NULL;
96 double sx, sy;
97 struct sway_cursor *cursor = seat->cursor;
98 struct sway_node *node = node_at_coords(seat,
99 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
100 // Damage the old location
101 desktop_damage_box(&e->drop_box);
102
103 if (!node) {
104 // Eg. hovered over a layer surface such as swaybar
105 e->target_node = NULL;
106 e->target_edge = WLR_EDGE_NONE;
107 return;
108 }
109
110 if (node->type == N_WORKSPACE) {
111 // Emtpy workspace
112 e->target_node = node;
113 e->target_edge = WLR_EDGE_NONE;
114 workspace_get_box(node->sway_workspace, &e->drop_box);
115 desktop_damage_box(&e->drop_box);
116 return;
117 }
118
119 // Deny moving within own workspace if this is the only child
120 struct sway_container *con = node->sway_container;
121 if (workspace_num_tiling_views(e->con->workspace) == 1 &&
122 con->workspace == e->con->workspace) {
123 e->target_node = NULL;
124 e->target_edge = WLR_EDGE_NONE;
125 return;
126 }
127
128 // Traverse the ancestors, trying to find a layout container perpendicular
129 // to the edge. Eg. close to the top or bottom of a horiz layout.
130 while (con) {
131 enum wlr_edges edge = WLR_EDGE_NONE;
132 enum sway_container_layout layout = container_parent_layout(con);
133 struct wlr_box parent;
134 con->parent ? container_get_box(con->parent, &parent) :
135 workspace_get_box(con->workspace, &parent);
136 if (layout == L_HORIZ || layout == L_TABBED) {
137 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) {
138 edge = WLR_EDGE_TOP;
139 } else if (cursor->cursor->y > parent.y + parent.height
140 - DROP_LAYOUT_BORDER) {
141 edge = WLR_EDGE_BOTTOM;
142 }
143 } else if (layout == L_VERT || layout == L_STACKED) {
144 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) {
145 edge = WLR_EDGE_LEFT;
146 } else if (cursor->cursor->x > parent.x + parent.width
147 - DROP_LAYOUT_BORDER) {
148 edge = WLR_EDGE_RIGHT;
149 }
150 }
151 if (edge) {
152 e->target_node = node_get_parent(&con->node);
153 e->target_edge = edge;
154 node_get_box(e->target_node, &e->drop_box);
155 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
156 desktop_damage_box(&e->drop_box);
157 return;
158 }
159 con = con->parent;
160 }
161
162 // Use the hovered view - but we must be over the actual surface
163 con = node->sway_container;
164 if (!con->view->surface || node == &e->con->node) {
165 e->target_node = NULL;
166 e->target_edge = WLR_EDGE_NONE;
167 return;
168 }
169
170 // Find the closest edge
171 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
172 size_t closest_dist = INT_MAX;
173 size_t dist;
174 e->target_edge = WLR_EDGE_NONE;
175 if ((dist = cursor->cursor->y - con->y) < closest_dist) {
176 closest_dist = dist;
177 e->target_edge = WLR_EDGE_TOP;
178 }
179 if ((dist = cursor->cursor->x - con->x) < closest_dist) {
180 closest_dist = dist;
181 e->target_edge = WLR_EDGE_LEFT;
182 }
183 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) {
184 closest_dist = dist;
185 e->target_edge = WLR_EDGE_RIGHT;
186 }
187 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) {
188 closest_dist = dist;
189 e->target_edge = WLR_EDGE_BOTTOM;
190 }
191
192 if (closest_dist > thickness) {
193 e->target_edge = WLR_EDGE_NONE;
194 }
195
196 e->target_node = node;
197 e->drop_box.x = con->content_x;
198 e->drop_box.y = con->content_y;
199 e->drop_box.width = con->content_width;
200 e->drop_box.height = con->content_height;
201 resize_box(&e->drop_box, e->target_edge, thickness);
202 desktop_damage_box(&e->drop_box);
203}
204
205static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
206 struct seatop_move_tiling_event *e = seat->seatop_data;
207 if (e->threshold_reached) {
208 handle_motion_postthreshold(seat);
209 } else {
210 handle_motion_prethreshold(seat);
211 }
212}
213
214static void handle_abort(struct sway_seat *seat) {
215 cursor_set_image(seat->cursor, "left_ptr", NULL);
216}
217
218static bool is_parallel(enum sway_container_layout layout,
219 enum wlr_edges edge) {
220 bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED;
221 bool edge_is_horiz = edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT;
222 return layout_is_horiz == edge_is_horiz;
223}
224
225static void handle_finish(struct sway_seat *seat) {
226 struct seatop_move_tiling_event *e = seat->seatop_data;
227
228 if (!e->target_node) {
229 handle_abort(seat);
230 return;
231 }
232
233 struct sway_container *con = e->con;
234 struct sway_container *old_parent = con->parent;
235 struct sway_workspace *old_ws = con->workspace;
236 struct sway_node *target_node = e->target_node;
237 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
238 target_node->sway_workspace : target_node->sway_container->workspace;
239 enum wlr_edges edge = e->target_edge;
240 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
241
242 container_detach(con);
243
244 // Moving container into empty workspace
245 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
246 workspace_add_tiling(new_ws, con);
247 } else if (target_node->type == N_CONTAINER) {
248 // Moving container before/after another
249 struct sway_container *target = target_node->sway_container;
250 enum sway_container_layout layout = container_parent_layout(target);
251 if (edge && !is_parallel(layout, edge)) {
252 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
253 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
254 container_split(target, new_layout);
255 }
256 container_add_sibling(target, con, after);
257 } else {
258 // Target is a workspace which requires splitting
259 enum sway_container_layout new_layout = edge == WLR_EDGE_TOP ||
260 edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ;
261 workspace_split(new_ws, new_layout);
262 workspace_insert_tiling(new_ws, con, after);
263 }
264
265 if (old_parent) {
266 container_reap_empty(old_parent);
267 }
268
269 // This is a bit dirty, but we'll set the dimensions to that of a sibling.
270 // I don't think there's any other way to make it consistent without
271 // changing how we auto-size containers.
272 list_t *siblings = container_get_siblings(con);
273 if (siblings->length > 1) {
274 int index = list_find(siblings, con);
275 struct sway_container *sibling = index == 0 ?
276 siblings->items[1] : siblings->items[index - 1];
277 con->width = sibling->width;
278 con->height = sibling->height;
279 }
280
281 arrange_workspace(old_ws);
282 if (new_ws != old_ws) {
283 arrange_workspace(new_ws);
284 }
285
286 cursor_set_image(seat->cursor, "left_ptr", NULL);
287}
288
289static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
290 struct seatop_move_tiling_event *e = seat->seatop_data;
291 if (e->target_node == &con->node) { // Drop target
292 e->target_node = NULL;
293 }
294 if (e->con == con) { // The container being moved
295 seatop_abort(seat);
296 }
297}
298
299static const struct sway_seatop_impl seatop_impl = {
300 .motion = handle_motion,
301 .finish = handle_finish,
302 .abort = handle_abort,
303 .unref = handle_unref,
304 .render = handle_render,
305};
306
307void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
308 struct sway_container *con, uint32_t button) {
309 seatop_abort(seat);
310
311 struct seatop_move_tiling_event *e =
312 calloc(1, sizeof(struct seatop_move_tiling_event));
313 if (!e) {
314 return;
315 }
316 e->con = con;
317 e->ref_lx = seat->cursor->cursor->x;
318 e->ref_ly = seat->cursor->cursor->y;
319
320 seat->seatop_impl = &seatop_impl;
321 seat->seatop_data = e;
322 seat->seatop_button = button;
323
324 container_raise_floating(con);
325}
326
327void seatop_begin_move_tiling(struct sway_seat *seat,
328 struct sway_container *con, uint32_t button) {
329 seatop_begin_move_tiling_threshold(seat, con, button);
330 struct seatop_move_tiling_event *e = seat->seatop_data;
331 if (e) {
332 e->threshold_reached = true;
333 cursor_set_image(seat->cursor, "grab", NULL);
334 }
335}
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
new file mode 100644
index 00000000..12851b40
--- /dev/null
+++ b/sway/input/seatop_resize_floating.c
@@ -0,0 +1,199 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h>
3#include <wlr/types/wlr_cursor.h>
4#include <wlr/types/wlr_xcursor_manager.h>
5#include "sway/input/cursor.h"
6#include "sway/input/seat.h"
7#include "sway/tree/arrange.h"
8#include "sway/tree/view.h"
9#include "sway/tree/workspace.h"
10
11struct seatop_resize_floating_event {
12 struct sway_container *con;
13 enum wlr_edges edge;
14 bool preserve_ratio;
15 double ref_lx, ref_ly; // cursor's x/y at start of op
16 double ref_width, ref_height; // container's size at start of op
17 double ref_con_lx, ref_con_ly; // container's x/y at start of op
18};
19
20static void calculate_floating_constraints(struct sway_container *con,
21 int *min_width, int *max_width, int *min_height, int *max_height) {
22 if (config->floating_minimum_width == -1) { // no minimum
23 *min_width = 0;
24 } else if (config->floating_minimum_width == 0) { // automatic
25 *min_width = 75;
26 } else {
27 *min_width = config->floating_minimum_width;
28 }
29
30 if (config->floating_minimum_height == -1) { // no minimum
31 *min_height = 0;
32 } else if (config->floating_minimum_height == 0) { // automatic
33 *min_height = 50;
34 } else {
35 *min_height = config->floating_minimum_height;
36 }
37
38 if (config->floating_maximum_width == -1) { // no maximum
39 *max_width = INT_MAX;
40 } else if (config->floating_maximum_width == 0) { // automatic
41 *max_width = con->workspace->width;
42 } else {
43 *max_width = config->floating_maximum_width;
44 }
45
46 if (config->floating_maximum_height == -1) { // no maximum
47 *max_height = INT_MAX;
48 } else if (config->floating_maximum_height == 0) { // automatic
49 *max_height = con->workspace->height;
50 } else {
51 *max_height = config->floating_maximum_height;
52 }
53}
54
55static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
56 struct seatop_resize_floating_event *e = seat->seatop_data;
57 struct sway_container *con = e->con;
58 enum wlr_edges edge = e->edge;
59 struct sway_cursor *cursor = seat->cursor;
60
61 // The amount the mouse has moved since the start of the resize operation
62 // Positive is down/right
63 double mouse_move_x = cursor->cursor->x - e->ref_lx;
64 double mouse_move_y = cursor->cursor->y - e->ref_ly;
65
66 if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
67 mouse_move_x = 0;
68 }
69 if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
70 mouse_move_y = 0;
71 }
72
73 double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x;
74 double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y;
75
76 if (e->preserve_ratio) {
77 double x_multiplier = grow_width / e->ref_width;
78 double y_multiplier = grow_height / e->ref_height;
79 double max_multiplier = fmax(x_multiplier, y_multiplier);
80 grow_width = e->ref_width * max_multiplier;
81 grow_height = e->ref_height * max_multiplier;
82 }
83
84 // Determine new width/height, and accommodate for floating min/max values
85 double width = e->ref_width + grow_width;
86 double height = e->ref_height + grow_height;
87 int min_width, max_width, min_height, max_height;
88 calculate_floating_constraints(con, &min_width, &max_width,
89 &min_height, &max_height);
90 width = fmax(min_width, fmin(width, max_width));
91 height = fmax(min_height, fmin(height, max_height));
92
93 // Apply the view's min/max size
94 if (con->view) {
95 double view_min_width, view_max_width, view_min_height, view_max_height;
96 view_get_constraints(con->view, &view_min_width, &view_max_width,
97 &view_min_height, &view_max_height);
98 width = fmax(view_min_width, fmin(width, view_max_width));
99 height = fmax(view_min_height, fmin(height, view_max_height));
100 }
101
102 // Recalculate these, in case we hit a min/max limit
103 grow_width = width - e->ref_width;
104 grow_height = height - e->ref_height;
105
106 // Determine grow x/y values - these are relative to the container's x/y at
107 // the start of the resize operation.
108 double grow_x = 0, grow_y = 0;
109 if (edge & WLR_EDGE_LEFT) {
110 grow_x = -grow_width;
111 } else if (edge & WLR_EDGE_RIGHT) {
112 grow_x = 0;
113 } else {
114 grow_x = -grow_width / 2;
115 }
116 if (edge & WLR_EDGE_TOP) {
117 grow_y = -grow_height;
118 } else if (edge & WLR_EDGE_BOTTOM) {
119 grow_y = 0;
120 } else {
121 grow_y = -grow_height / 2;
122 }
123
124 // Determine the amounts we need to bump everything relative to the current
125 // size.
126 int relative_grow_width = width - con->width;
127 int relative_grow_height = height - con->height;
128 int relative_grow_x = (e->ref_con_lx + grow_x) - con->x;
129 int relative_grow_y = (e->ref_con_ly + grow_y) - con->y;
130
131 // Actually resize stuff
132 con->x += relative_grow_x;
133 con->y += relative_grow_y;
134 con->width += relative_grow_width;
135 con->height += relative_grow_height;
136
137 con->content_x += relative_grow_x;
138 con->content_y += relative_grow_y;
139 con->content_width += relative_grow_width;
140 con->content_height += relative_grow_height;
141
142 arrange_container(con);
143}
144
145static void handle_finish(struct sway_seat *seat) {
146 cursor_set_image(seat->cursor, "left_ptr", NULL);
147}
148
149static void handle_abort(struct sway_seat *seat) {
150 cursor_set_image(seat->cursor, "left_ptr", NULL);
151}
152
153static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
154 struct seatop_resize_floating_event *e = seat->seatop_data;
155 if (e->con == con) {
156 seatop_abort(seat);
157 }
158}
159
160static const struct sway_seatop_impl seatop_impl = {
161 .motion = handle_motion,
162 .finish = handle_finish,
163 .abort = handle_abort,
164 .unref = handle_unref,
165};
166
167void seatop_begin_resize_floating(struct sway_seat *seat,
168 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
169 seatop_abort(seat);
170
171 struct seatop_resize_floating_event *e =
172 calloc(1, sizeof(struct seatop_resize_floating_event));
173 if (!e) {
174 return;
175 }
176 e->con = con;
177
178 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
179 e->preserve_ratio = keyboard &&
180 (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT);
181
182 e->edge = edge == WLR_EDGE_NONE ? WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT : edge;
183 e->ref_lx = seat->cursor->cursor->x;
184 e->ref_ly = seat->cursor->cursor->y;
185 e->ref_con_lx = con->x;
186 e->ref_con_ly = con->y;
187 e->ref_width = con->width;
188 e->ref_height = con->height;
189
190 seat->seatop_impl = &seatop_impl;
191 seat->seatop_data = e;
192 seat->seatop_button = button;
193
194 container_raise_floating(con);
195
196 const char *image = edge == WLR_EDGE_NONE ?
197 "se-resize" : wlr_xcursor_get_resize_name(edge);
198 cursor_set_image(seat->cursor, image, NULL);
199}
diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c
new file mode 100644
index 00000000..30431f04
--- /dev/null
+++ b/sway/input/seatop_resize_tiling.c
@@ -0,0 +1,92 @@
1#define _POSIX_C_SOURCE 200809L
2#include <wlr/types/wlr_cursor.h>
3#include "sway/commands.h"
4#include "sway/input/cursor.h"
5#include "sway/input/seat.h"
6
7struct seatop_resize_tiling_event {
8 struct sway_container *con;
9 enum wlr_edges edge;
10 double ref_lx, ref_ly; // cursor's x/y at start of op
11 double ref_width, ref_height; // container's size at start of op
12 double ref_con_lx, ref_con_ly; // container's x/y at start of op
13};
14
15static void handle_motion(struct sway_seat *seat, uint32_t time_msec) {
16 struct seatop_resize_tiling_event *e = seat->seatop_data;
17 int amount_x = 0;
18 int amount_y = 0;
19 int moved_x = seat->cursor->cursor->x - e->ref_lx;
20 int moved_y = seat->cursor->cursor->y - e->ref_ly;
21 enum wlr_edges edge_x = WLR_EDGE_NONE;
22 enum wlr_edges edge_y = WLR_EDGE_NONE;
23 struct sway_container *con = e->con;
24
25 if (e->edge & WLR_EDGE_TOP) {
26 amount_y = (e->ref_height - moved_y) - con->height;
27 edge_y = WLR_EDGE_TOP;
28 } else if (e->edge & WLR_EDGE_BOTTOM) {
29 amount_y = (e->ref_height + moved_y) - con->height;
30 edge_y = WLR_EDGE_BOTTOM;
31 }
32 if (e->edge & WLR_EDGE_LEFT) {
33 amount_x = (e->ref_width - moved_x) - con->width;
34 edge_x = WLR_EDGE_LEFT;
35 } else if (e->edge & WLR_EDGE_RIGHT) {
36 amount_x = (e->ref_width + moved_x) - con->width;
37 edge_x = WLR_EDGE_RIGHT;
38 }
39
40 if (amount_x != 0) {
41 container_resize_tiled(e->con, edge_x, amount_x);
42 }
43 if (amount_y != 0) {
44 container_resize_tiled(e->con, edge_y, amount_y);
45 }
46}
47
48static void handle_finish(struct sway_seat *seat) {
49 cursor_set_image(seat->cursor, "left_ptr", NULL);
50}
51
52static void handle_abort(struct sway_seat *seat) {
53 cursor_set_image(seat->cursor, "left_ptr", NULL);
54}
55
56static void handle_unref(struct sway_seat *seat, struct sway_container *con) {
57 struct seatop_resize_tiling_event *e = seat->seatop_data;
58 if (e->con == con) {
59 seatop_abort(seat);
60 }
61}
62
63static const struct sway_seatop_impl seatop_impl = {
64 .motion = handle_motion,
65 .finish = handle_finish,
66 .abort = handle_abort,
67 .unref = handle_unref,
68};
69
70void seatop_begin_resize_tiling(struct sway_seat *seat,
71 struct sway_container *con, uint32_t button, enum wlr_edges edge) {
72 seatop_abort(seat);
73
74 struct seatop_resize_tiling_event *e =
75 calloc(1, sizeof(struct seatop_resize_tiling_event));
76 if (!e) {
77 return;
78 }
79 e->con = con;
80 e->edge = edge;
81
82 e->ref_lx = seat->cursor->cursor->x;
83 e->ref_ly = seat->cursor->cursor->y;
84 e->ref_con_lx = con->x;
85 e->ref_con_ly = con->y;
86 e->ref_width = con->width;
87 e->ref_height = con->height;
88
89 seat->seatop_impl = &seatop_impl;
90 seat->seatop_data = e;
91 seat->seatop_button = button;
92}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 53e0e335..15f89f65 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -11,6 +11,7 @@
11#include "sway/output.h" 11#include "sway/output.h"
12#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h" 13#include "sway/input/seat.h"
14#include <wlr/backend/libinput.h>
14#include <wlr/types/wlr_box.h> 15#include <wlr/types/wlr_box.h>
15#include <wlr/types/wlr_output.h> 16#include <wlr/types/wlr_output.h>
16#include <xkbcommon/xkbcommon.h> 17#include <xkbcommon/xkbcommon.h>
@@ -598,6 +599,26 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
598 } 599 }
599 } 600 }
600 601
602 if (wlr_input_device_is_libinput(device->wlr_device)) {
603 struct libinput_device *libinput_dev;
604 libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
605
606 const char *events = "unknown";
607 switch (libinput_device_config_send_events_get_mode(libinput_dev)) {
608 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
609 events = "enabled";
610 break;
611 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
612 events = "disabled_on_external_mouse";
613 break;
614 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
615 events = "disabled";
616 break;
617 }
618 json_object_object_add(object, "libinput_send_events",
619 json_object_new_string(events));
620 }
621
601 return object; 622 return object;
602} 623}
603 624
@@ -660,6 +681,10 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
660 } 681 }
661 json_object_object_add(json, "bar_height", 682 json_object_object_add(json, "bar_height",
662 json_object_new_int(bar->height)); 683 json_object_new_int(bar->height));
684 json_object_object_add(json, "status_padding",
685 json_object_new_int(bar->status_padding));
686 json_object_object_add(json, "status_edge_padding",
687 json_object_new_int(bar->status_edge_padding));
663 json_object_object_add(json, "wrap_scroll", 688 json_object_object_add(json, "wrap_scroll",
664 json_object_new_boolean(bar->wrap_scroll)); 689 json_object_new_boolean(bar->wrap_scroll));
665 json_object_object_add(json, "workspace_buttons", 690 json_object_object_add(json, "workspace_buttons",
diff --git a/sway/meson.build b/sway/meson.build
index 98676ce0..b49d550a 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -23,6 +23,11 @@ sway_sources = files(
23 23
24 'input/input-manager.c', 24 'input/input-manager.c',
25 'input/seat.c', 25 'input/seat.c',
26 'input/seatop_down.c',
27 'input/seatop_move_floating.c',
28 'input/seatop_move_tiling.c',
29 'input/seatop_resize_floating.c',
30 'input/seatop_resize_tiling.c',
26 'input/cursor.c', 31 'input/cursor.c',
27 'input/keyboard.c', 32 'input/keyboard.c',
28 33
@@ -116,6 +121,8 @@ sway_sources = files(
116 'commands/bar/position.c', 121 'commands/bar/position.c',
117 'commands/bar/separator_symbol.c', 122 'commands/bar/separator_symbol.c',
118 'commands/bar/status_command.c', 123 'commands/bar/status_command.c',
124 'commands/bar/status_edge_padding.c',
125 'commands/bar/status_padding.c',
119 'commands/bar/strip_workspace_numbers.c', 126 'commands/bar/strip_workspace_numbers.c',
120 'commands/bar/strip_workspace_name.c', 127 'commands/bar/strip_workspace_name.c',
121 'commands/bar/swaybar_command.c', 128 'commands/bar/swaybar_command.c',
diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd
index 2357591d..e1a4a937 100644
--- a/sway/sway-bar.5.scd
+++ b/sway/sway-bar.5.scd
@@ -69,7 +69,7 @@ Sway allows configuring swaybar in the sway configuration file.
69 use. 69 use.
70 70
71*height* <height> 71*height* <height>
72 Sets the height of the bar. Default height will match the font size. 72 Sets the height of the bar. Default height (0) will match the font size.
73 73
74*bindsym* [--release] button<n> <command> 74*bindsym* [--release] button<n> <command>
75 Executes _command_ when mouse button _n_ has been pressed (or if _released_ 75 Executes _command_ when mouse button _n_ has been pressed (or if _released_
@@ -92,6 +92,16 @@ Sway allows configuring swaybar in the sway configuration file.
92*modifier* <Modifier>|none 92*modifier* <Modifier>|none
93 Specifies the modifier key that shows a hidden bar. Default is _Mod4_. 93 Specifies the modifier key that shows a hidden bar. Default is _Mod4_.
94 94
95*status\_padding* <padding>
96 Sets the vertical padding that is used for the status line. The default is
97 _1_. If _padding_ is _0_, blocks will be able to take up the full height of
98 the bar. This value will be multiplied by the output scale.
99
100*status\_edge\_padding* <padding>
101 Sets the padding that is used when the status line is at the right edge of
102 the bar. This value will be multiplied by the output scale. The default is
103 _3_.
104
95## TRAY 105## TRAY
96 106
97Swaybar provides a system tray where third-party applications may place icons. 107Swaybar provides a system tray where third-party applications may place icons.
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 044057f2..c2673f2a 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -82,9 +82,12 @@ The following commands may only be used in the configuration file.
82*input* <identifier> dwt enabled|disabled 82*input* <identifier> dwt enabled|disabled
83 Enables or disables disable-while-typing for the specified input device. 83 Enables or disables disable-while-typing for the specified input device.
84 84
85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse 85*input* <identifier> events enabled|disabled|disabled\_on\_external\_mouse|toggle
86 Enables or disables send\_events for specified input device. (Disabling 86 Enables or disables send\_events for specified input device. Disabling
87 send\_events disables the input device) 87 send\_events disables the input device. The _toggle_ option cannot be used
88 in the config. The order is enabled, disabled\_on\_external\_mouse,
89 disabled, (loop back to enabled). Any mode which is not supported by the
90 device will be skipped during the toggle.
88 91
89*input* <identifier> left\_handed enabled|disabled 92*input* <identifier> left\_handed enabled|disabled
90 Enables or disables left handed mode for specified input device. 93 Enables or disables left handed mode for specified input device.
@@ -105,10 +108,11 @@ The following commands may only be used in the configuration file.
105*input* <identifier> repeat\_rate <characters per second> 108*input* <identifier> repeat\_rate <characters per second>
106 Sets the frequency of key repeats once the repeat\_delay has passed. 109 Sets the frequency of key repeats once the repeat\_delay has passed.
107 110
108*input* <identifier> scroll\_button <button\_identifier> 111*input* <identifier> scroll\_button disable|button[1-3,8,9]|<event-code-or-name>
109 Sets button used for scroll\_method on\_button\_down. The button identifier 112 Sets the button used for scroll\_method on\_button\_down. The button can
110 can be obtained from `libinput debug-events`. 113 be given as an event name or code, which can be obtained from `libinput
111 If set to 0, it disables the scroll\_button on\_button\_down. 114 debug-events`, or as a x11 mouse button (button[1-3,8,9]). If set to
115 _disable_, it disables the scroll\_method on\_button\_down.
112 116
113*input* <identifier> scroll\_factor <floating point value> 117*input* <identifier> scroll\_factor <floating point value>
114 Changes the scroll factor for the specified input device. Scroll speed will 118 Changes the scroll factor for the specified input device. Scroll speed will
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 99262356..d9c721f5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -864,15 +864,7 @@ bool container_has_urgent_child(struct sway_container *container) {
864void container_end_mouse_operation(struct sway_container *container) { 864void container_end_mouse_operation(struct sway_container *container) {
865 struct sway_seat *seat; 865 struct sway_seat *seat;
866 wl_list_for_each(seat, &server.input->seats, link) { 866 wl_list_for_each(seat, &server.input->seats, link) {
867 if (seat->op_container == container) { 867 seatop_unref(seat, container);
868 seat->op_target_node = NULL; // ensure tiling move doesn't apply
869 seat_end_mouse_operation(seat);
870 }
871 // If the user is doing a tiling drag over this container,
872 // keep the operation active but unset the target container.
873 if (seat->op_target_node == &container->node) {
874 seat->op_target_node = NULL;
875 }
876 } 868 }
877} 869}
878 870
@@ -1384,3 +1376,16 @@ void container_update_marks_textures(struct sway_container *con) {
1384 &config->border_colors.urgent); 1376 &config->border_colors.urgent);
1385 container_damage_whole(con); 1377 container_damage_whole(con);
1386} 1378}
1379
1380void container_raise_floating(struct sway_container *con) {
1381 // Bring container to front by putting it at the end of the floating list.
1382 struct sway_container *floater = con;
1383 while (floater->parent) {
1384 floater = floater->parent;
1385 }
1386 if (container_is_floating(floater)) {
1387 list_move_to_end(floater->workspace->floating, floater);
1388 node_set_dirty(&floater->workspace->node);
1389 }
1390}
1391
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 7aed4dca..d36367fc 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -59,6 +59,7 @@ static void swaybar_output_free(struct swaybar_output *output) {
59 free_workspaces(&output->workspaces); 59 free_workspaces(&output->workspaces);
60 wl_list_remove(&output->link); 60 wl_list_remove(&output->link);
61 free(output->name); 61 free(output->name);
62 free(output->identifier);
62 free(output); 63 free(output);
63} 64}
64 65
@@ -166,13 +167,15 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
166 return visible; 167 return visible;
167} 168}
168 169
169static bool bar_uses_output(struct swaybar *bar, const char *name) { 170static bool bar_uses_output(struct swaybar_output *output) {
170 if (bar->config->all_outputs) { 171 if (output->bar->config->all_outputs) {
171 return true; 172 return true;
172 } 173 }
174 char *identifier = output->identifier;
173 struct config_output *coutput; 175 struct config_output *coutput;
174 wl_list_for_each(coutput, &bar->config->outputs, link) { 176 wl_list_for_each(coutput, &output->bar->config->outputs, link) {
175 if (strcmp(coutput->name, name) == 0) { 177 if (strcmp(coutput->name, output->name) == 0 ||
178 (identifier && strcmp(coutput->name, identifier) == 0)) {
176 return true; 179 return true;
177 } 180 }
178 } 181 }
@@ -233,7 +236,7 @@ static void xdg_output_handle_done(void *data,
233 struct swaybar *bar = output->bar; 236 struct swaybar *bar = output->bar;
234 237
235 assert(output->name != NULL); 238 assert(output->name != NULL);
236 if (!bar_uses_output(bar, output->name)) { 239 if (!bar_uses_output(output)) {
237 swaybar_output_free(output); 240 swaybar_output_free(output);
238 return; 241 return;
239 } 242 }
@@ -258,7 +261,22 @@ static void xdg_output_handle_name(void *data,
258 261
259static void xdg_output_handle_description(void *data, 262static void xdg_output_handle_description(void *data,
260 struct zxdg_output_v1 *xdg_output, const char *description) { 263 struct zxdg_output_v1 *xdg_output, const char *description) {
261 // Who cares 264 // wlroots currently sets the description to `make model serial (name)`
265 // If this changes in the future, this will need to be modified.
266 struct swaybar_output *output = data;
267 free(output->identifier);
268 output->identifier = NULL;
269 char *paren = strrchr(description, '(');
270 if (paren) {
271 size_t length = paren - description;
272 output->identifier = malloc(length);
273 if (!output->identifier) {
274 wlr_log(WLR_ERROR, "Failed to allocate output identifier");
275 return;
276 }
277 strncpy(output->identifier, description, length);
278 output->identifier[length - 1] = '\0';
279 }
262} 280}
263 281
264struct zxdg_output_v1_listener xdg_output_listener = { 282struct zxdg_output_v1_listener xdg_output_listener = {
diff --git a/swaybar/config.c b/swaybar/config.c
index 9cafe061..d4cc9b1a 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -37,6 +37,8 @@ struct swaybar_config *init_config(void) {
37 config->workspace_buttons = true; 37 config->workspace_buttons = true;
38 config->bindings = create_list(); 38 config->bindings = create_list();
39 wl_list_init(&config->outputs); 39 wl_list_init(&config->outputs);
40 config->status_padding = 1;
41 config->status_edge_padding = 3;
40 42
41 /* height */ 43 /* height */
42 config->height = 0; 44 config->height = 0;
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 8e7a542e..bc5c28b4 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -157,7 +157,7 @@ static bool ipc_parse_config(
157 json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons; 157 json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons;
158 json_object *strip_workspace_numbers, *strip_workspace_name; 158 json_object *strip_workspace_numbers, *strip_workspace_name;
159 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol; 159 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol;
160 json_object *outputs, *bindings; 160 json_object *outputs, *bindings, *status_padding, *status_edge_padding;
161 json_object_object_get_ex(bar_config, "mode", &mode); 161 json_object_object_get_ex(bar_config, "mode", &mode);
162 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state); 162 json_object_object_get_ex(bar_config, "hidden_state", &hidden_state);
163 json_object_object_get_ex(bar_config, "position", &position); 163 json_object_object_get_ex(bar_config, "position", &position);
@@ -176,6 +176,9 @@ static bool ipc_parse_config(
176 json_object_object_get_ex(bar_config, "outputs", &outputs); 176 json_object_object_get_ex(bar_config, "outputs", &outputs);
177 json_object_object_get_ex(bar_config, "pango_markup", &markup); 177 json_object_object_get_ex(bar_config, "pango_markup", &markup);
178 json_object_object_get_ex(bar_config, "bindings", &bindings); 178 json_object_object_get_ex(bar_config, "bindings", &bindings);
179 json_object_object_get_ex(bar_config, "status_padding", &status_padding);
180 json_object_object_get_ex(bar_config, "status_edge_padding",
181 &status_edge_padding);
179 if (status_command) { 182 if (status_command) {
180 free(config->status_command); 183 free(config->status_command);
181 config->status_command = strdup(json_object_get_string(status_command)); 184 config->status_command = strdup(json_object_get_string(status_command));
@@ -209,6 +212,12 @@ static bool ipc_parse_config(
209 if (bar_height) { 212 if (bar_height) {
210 config->height = json_object_get_int(bar_height); 213 config->height = json_object_get_int(bar_height);
211 } 214 }
215 if (status_padding) {
216 config->status_padding = json_object_get_int(status_padding);
217 }
218 if (status_edge_padding) {
219 config->status_edge_padding = json_object_get_int(status_edge_padding);
220 }
212 if (gaps) { 221 if (gaps) {
213 json_object *top = json_object_object_get(gaps, "top"); 222 json_object *top = json_object_object_get(gaps, "top");
214 if (top) { 223 if (top) {
diff --git a/swaybar/render.c b/swaybar/render.c
index 7cbcea07..12dd3b07 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -35,7 +35,8 @@ static uint32_t render_status_line_error(cairo_t *cairo,
35 cairo_set_source_u32(cairo, 0xFF0000FF); 35 cairo_set_source_u32(cairo, 0xFF0000FF);
36 36
37 int margin = 3 * output->scale; 37 int margin = 3 * output->scale;
38 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 38 double ws_vertical_padding =
39 output->bar->config->status_padding * output->scale;
39 40
40 char *font = output->bar->config->font; 41 char *font = output->bar->config->font;
41 int text_width, text_height; 42 int text_width, text_height;
@@ -44,7 +45,8 @@ static uint32_t render_status_line_error(cairo_t *cairo,
44 45
45 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 46 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
46 uint32_t ideal_surface_height = ideal_height / output->scale; 47 uint32_t ideal_surface_height = ideal_height / output->scale;
47 if (output->height < ideal_surface_height) { 48 if (!output->bar->config->height &&
49 output->height < ideal_surface_height) {
48 return ideal_surface_height; 50 return ideal_surface_height;
49 } 51 }
50 *x -= text_width + margin; 52 *x -= text_width + margin;
@@ -71,12 +73,13 @@ static uint32_t render_status_line_text(cairo_t *cairo,
71 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 73 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
72 output->scale, config->pango_markup, "%s", text); 74 output->scale, config->pango_markup, "%s", text);
73 75
74 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 76 double ws_vertical_padding = config->status_padding * output->scale;
75 int margin = 3 * output->scale; 77 int margin = 3 * output->scale;
76 78
77 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 79 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
78 uint32_t ideal_surface_height = ideal_height / output->scale; 80 uint32_t ideal_surface_height = ideal_height / output->scale;
79 if (output->height < ideal_surface_height) { 81 if (!output->bar->config->height &&
82 output->height < ideal_surface_height) {
80 return ideal_surface_height; 83 return ideal_surface_height;
81 } 84 }
82 85
@@ -153,7 +156,7 @@ static uint32_t render_status_block(cairo_t *cairo,
153 output->scale, block->markup, "%s", block->full_text); 156 output->scale, block->markup, "%s", block->full_text);
154 157
155 int margin = 3 * output->scale; 158 int margin = 3 * output->scale;
156 double ws_vertical_padding = WS_VERTICAL_PADDING * 2 * output->scale; 159 double ws_vertical_padding = config->status_padding * output->scale;
157 160
158 int width = text_width; 161 int width = text_width;
159 if (width < block->min_width) { 162 if (width < block->min_width) {
@@ -163,7 +166,8 @@ static uint32_t render_status_block(cairo_t *cairo,
163 double block_width = width; 166 double block_width = width;
164 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 167 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
165 uint32_t ideal_surface_height = ideal_height / output->scale; 168 uint32_t ideal_surface_height = ideal_height / output->scale;
166 if (output->height < ideal_surface_height) { 169 if (!output->bar->config->height &&
170 output->height < ideal_surface_height) {
167 return ideal_surface_height; 171 return ideal_surface_height;
168 } 172 }
169 173
@@ -185,7 +189,8 @@ static uint32_t render_status_block(cairo_t *cairo,
185 output->scale, false, "%s", config->sep_symbol); 189 output->scale, false, "%s", config->sep_symbol);
186 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 190 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
187 uint32_t _ideal_surface_height = _ideal_height / output->scale; 191 uint32_t _ideal_surface_height = _ideal_height / output->scale;
188 if (output->height < _ideal_surface_height) { 192 if (!output->bar->config->height &&
193 output->height < _ideal_surface_height) {
189 return _ideal_surface_height; 194 return _ideal_surface_height;
190 } 195 }
191 if (sep_width > sep_block_width) { 196 if (sep_width > sep_block_width) {
@@ -193,8 +198,8 @@ static uint32_t render_status_block(cairo_t *cairo,
193 } 198 }
194 } 199 }
195 *x -= sep_block_width; 200 *x -= sep_block_width;
196 } else { 201 } else if (config->status_edge_padding) {
197 *x -= margin; 202 *x -= config->status_edge_padding * output->scale;
198 } 203 }
199 204
200 uint32_t height = output->height * output->scale; 205 uint32_t height = output->height * output->scale;
@@ -212,8 +217,8 @@ static uint32_t render_status_block(cairo_t *cairo,
212 } 217 }
213 218
214 double x_pos = *x; 219 double x_pos = *x;
215 double y_pos = WS_VERTICAL_PADDING * output->scale; 220 double y_pos = ws_vertical_padding;
216 double render_height = height - ws_vertical_padding + output->scale; 221 double render_height = height - ws_vertical_padding * 2;
217 222
218 uint32_t bg_color = block->urgent 223 uint32_t bg_color = block->urgent
219 ? config->colors.urgent_workspace.background : block->background; 224 ? config->colors.urgent_workspace.background : block->background;
@@ -286,7 +291,7 @@ static uint32_t render_status_block(cairo_t *cairo,
286static uint32_t render_status_line_i3bar(cairo_t *cairo, 291static uint32_t render_status_line_i3bar(cairo_t *cairo,
287 struct swaybar_output *output, double *x) { 292 struct swaybar_output *output, double *x) {
288 uint32_t max_height = 0; 293 uint32_t max_height = 0;
289 bool edge = true; 294 bool edge = *x == output->width * output->scale;
290 struct i3bar_block *block; 295 struct i3bar_block *block;
291 wl_list_for_each(block, &output->bar->status->blocks, link) { 296 wl_list_for_each(block, &output->bar->status->blocks, link) {
292 uint32_t h = render_status_block(cairo, output, block, x, edge); 297 uint32_t h = render_status_block(cairo, output, block, x, edge);
@@ -332,7 +337,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
332 uint32_t ideal_height = text_height + ws_vertical_padding * 2 337 uint32_t ideal_height = text_height + ws_vertical_padding * 2
333 + border_width * 2; 338 + border_width * 2;
334 uint32_t ideal_surface_height = ideal_height / output->scale; 339 uint32_t ideal_surface_height = ideal_height / output->scale;
335 if (output->height < ideal_surface_height) { 340 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) {
336 return ideal_surface_height; 342 return ideal_surface_height;
337 } 343 }
338 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 344 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
@@ -398,7 +404,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
398 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 404 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
399 + border_width * 2; 405 + border_width * 2;
400 uint32_t ideal_surface_height = ideal_height / output->scale; 406 uint32_t ideal_surface_height = ideal_height / output->scale;
401 if (output->height < ideal_surface_height) { 407 if (!output->bar->config->height &&
408 output->height < ideal_surface_height) {
402 return ideal_surface_height; 409 return ideal_surface_height;
403 } 410 }
404 411
@@ -525,7 +532,7 @@ void render_frame(struct swaybar_output *output) {
525 cairo_restore(cairo); 532 cairo_restore(cairo);
526 uint32_t height = render_to_cairo(cairo, output); 533 uint32_t height = render_to_cairo(cairo, output);
527 int config_height = output->bar->config->height; 534 int config_height = output->bar->config->height;
528 if (config_height >= 0 && height < (uint32_t)config_height) { 535 if (config_height > 0) {
529 height = config_height; 536 height = config_height;
530 } 537 }
531 if (height != output->height || output->width == 0) { 538 if (height != output->height || output->width == 0) {
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 0c3517cb..d5d0c84e 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -101,13 +101,17 @@ void tray_in(int fd, short mask, void *data) {
101} 101}
102 102
103static int cmp_output(const void *item, const void *cmp_to) { 103static int cmp_output(const void *item, const void *cmp_to) {
104 return strcmp(item, cmp_to); 104 const struct swaybar_output *output = cmp_to;
105 if (output->identifier && strcmp(item, output->identifier) == 0) {
106 return 0;
107 }
108 return strcmp(item, output->name);
105} 109}
106 110
107uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { 111uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {
108 struct swaybar_config *config = output->bar->config; 112 struct swaybar_config *config = output->bar->config;
109 if (config->tray_outputs) { 113 if (config->tray_outputs) {
110 if (list_seq_find(config->tray_outputs, cmp_output, output->name) == -1) { 114 if (list_seq_find(config->tray_outputs, cmp_output, output) == -1) {
111 return 0; 115 return 0;
112 } 116 }
113 } // else display on all 117 } // else display on all
diff --git a/swayidle/main.c b/swayidle/main.c
deleted file mode 100644
index 41eecc41..00000000
--- a/swayidle/main.c
+++ /dev/null
@@ -1,483 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <getopt.h>
6#include <pthread.h>
7#include <signal.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/wait.h>
12#include <unistd.h>
13#include <wayland-client-protocol.h>
14#include <wayland-client.h>
15#include <wayland-server.h>
16#include <wayland-util.h>
17#include <wlr/config.h>
18#include <wlr/util/log.h>
19#include "config.h"
20#include "idle-client-protocol.h"
21#include "list.h"
22#if HAVE_SYSTEMD
23#include <systemd/sd-bus.h>
24#include <systemd/sd-login.h>
25#elif HAVE_ELOGIND
26#include <elogind/sd-bus.h>
27#include <elogind/sd-login.h>
28#endif
29
30static struct org_kde_kwin_idle *idle_manager = NULL;
31static struct wl_seat *seat = NULL;
32
33struct swayidle_state {
34 struct wl_display *display;
35 struct wl_event_loop *event_loop;
36 list_t *timeout_cmds; // struct swayidle_timeout_cmd *
37 char *lock_cmd;
38} state;
39
40struct swayidle_timeout_cmd {
41 int timeout, registered_timeout;
42 struct org_kde_kwin_idle_timeout *idle_timer;
43 char *idle_cmd;
44 char *resume_cmd;
45};
46
47void sway_terminate(int exit_code) {
48 wl_display_disconnect(state.display);
49 wl_event_loop_destroy(state.event_loop);
50 exit(exit_code);
51}
52
53static void cmd_exec(char *param) {
54 wlr_log(WLR_DEBUG, "Cmd exec %s", param);
55 pid_t pid = fork();
56 if (pid == 0) {
57 pid = fork();
58 if (pid == 0) {
59 char *const cmd[] = { "sh", "-c", param, NULL, };
60 execvp(cmd[0], cmd);
61 wlr_log_errno(WLR_ERROR, "execve failed!");
62 exit(1);
63 } else if (pid < 0) {
64 wlr_log_errno(WLR_ERROR, "fork failed");
65 exit(1);
66 }
67 exit(0);
68 } else if (pid < 0) {
69 wlr_log_errno(WLR_ERROR, "fork failed");
70 } else {
71 wlr_log(WLR_DEBUG, "Spawned process %s", param);
72 waitpid(pid, NULL, 0);
73 }
74}
75
76#if HAVE_SYSTEMD || HAVE_ELOGIND
77static int lock_fd = -1;
78static int ongoing_fd = -1;
79static struct sd_bus *bus = NULL;
80
81static int release_lock(void *data) {
82 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
83 if (ongoing_fd >= 0) {
84 close(ongoing_fd);
85 }
86 ongoing_fd = -1;
87 return 0;
88}
89
90static void acquire_sleep_lock(void) {
91 sd_bus_message *msg = NULL;
92 sd_bus_error error = SD_BUS_ERROR_NULL;
93 int ret = sd_bus_call_method(bus, "org.freedesktop.login1",
94 "/org/freedesktop/login1",
95 "org.freedesktop.login1.Manager", "Inhibit",
96 &error, &msg, "ssss", "sleep", "swayidle",
97 "Setup Up Lock Screen", "delay");
98 if (ret < 0) {
99 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s", error.message);
100 sd_bus_error_free(&error);
101 return;
102 }
103
104 ret = sd_bus_message_read(msg, "h", &lock_fd);
105 if (ret < 0) {
106 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
107 strerror(-ret));
108 sd_bus_error_free(&error);
109 sd_bus_message_unref(msg);
110 return;
111 } else {
112 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
113 }
114
115 // sd_bus_message_unref closes the file descriptor so we need
116 // to copy it beforehand
117 lock_fd = fcntl(lock_fd, F_DUPFD_CLOEXEC, 3);
118 if (lock_fd < 0) {
119 wlr_log(WLR_ERROR, "Failed to copy sleep lock fd: %s",
120 strerror(errno));
121 }
122
123 sd_bus_error_free(&error);
124 sd_bus_message_unref(msg);
125}
126
127static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
128 sd_bus_error *ret_error) {
129 /* "b" apparently reads into an int, not a bool */
130 int going_down = 1;
131 int ret = sd_bus_message_read(msg, "b", &going_down);
132 if (ret < 0) {
133 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
134 strerror(-ret));
135 }
136 wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);
137 if (!going_down) {
138 acquire_sleep_lock();
139 return 0;
140 }
141
142 ongoing_fd = lock_fd;
143
144 if (state.lock_cmd) {
145 cmd_exec(state.lock_cmd);
146 }
147
148 if (ongoing_fd >= 0) {
149 struct wl_event_source *source =
150 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
151 wl_event_source_timer_update(source, 1000);
152 }
153
154 wlr_log(WLR_DEBUG, "Prepare for sleep done");
155 return 0;
156}
157
158static int dbus_event(int fd, uint32_t mask, void *data) {
159 sd_bus *bus = data;
160
161 if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
162 sway_terminate(0);
163 }
164
165 int count = 0;
166 if (mask & WL_EVENT_READABLE) {
167 count = sd_bus_process(bus, NULL);
168 }
169 if (mask & WL_EVENT_WRITABLE) {
170 sd_bus_flush(bus);
171 }
172 if (mask == 0) {
173 sd_bus_flush(bus);
174 }
175
176 if (count < 0) {
177 wlr_log_errno(WLR_ERROR, "sd_bus_process failed, exiting");
178 sway_terminate(0);
179 }
180
181 return count;
182}
183
184static void setup_sleep_listener(void) {
185 int ret = sd_bus_default_system(&bus);
186 if (ret < 0) {
187 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
188 strerror(-ret));
189 return;
190 }
191
192 char str[256];
193 const char *fmt = "type='signal',"
194 "sender='org.freedesktop.login1',"
195 "interface='org.freedesktop.login1.%s',"
196 "member='%s'," "path='%s'";
197
198 snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep",
199 "/org/freedesktop/login1");
200 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
201 if (ret < 0) {
202 wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
203 return;
204 }
205 acquire_sleep_lock();
206
207 struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
208 sd_bus_get_fd(bus), WL_EVENT_READABLE, dbus_event, bus);
209 wl_event_source_check(source);
210}
211#endif
212
213static void handle_global(void *data, struct wl_registry *registry,
214 uint32_t name, const char *interface, uint32_t version) {
215 if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) {
216 idle_manager =
217 wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
218 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
219 seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
220 }
221}
222
223static void handle_global_remove(void *data, struct wl_registry *registry,
224 uint32_t name) {
225 // Who cares
226}
227
228static const struct wl_registry_listener registry_listener = {
229 .global = handle_global,
230 .global_remove = handle_global_remove,
231};
232
233static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener;
234
235static void register_timeout(struct swayidle_timeout_cmd *cmd,
236 int timeout) {
237 if (cmd->idle_timer != NULL) {
238 org_kde_kwin_idle_timeout_destroy(cmd->idle_timer);
239 cmd->idle_timer = NULL;
240 }
241 if (timeout < 0) {
242 wlr_log(WLR_DEBUG, "Not registering idle timeout");
243 return;
244 }
245 wlr_log(WLR_DEBUG, "Register with timeout: %d", timeout);
246 cmd->idle_timer =
247 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout);
248 org_kde_kwin_idle_timeout_add_listener(cmd->idle_timer,
249 &idle_timer_listener, cmd);
250 cmd->registered_timeout = timeout;
251}
252
253static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
254 struct swayidle_timeout_cmd *cmd = data;
255 wlr_log(WLR_DEBUG, "idle state");
256 if (cmd->idle_cmd) {
257 cmd_exec(cmd->idle_cmd);
258 }
259}
260
261static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
262 struct swayidle_timeout_cmd *cmd = data;
263 wlr_log(WLR_DEBUG, "active state");
264 if (cmd->registered_timeout != cmd->timeout) {
265 register_timeout(cmd, cmd->timeout);
266 }
267 if (cmd->resume_cmd) {
268 cmd_exec(cmd->resume_cmd);
269 }
270}
271
272static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
273 .idle = handle_idle,
274 .resumed = handle_resume,
275};
276
277static char *parse_command(int argc, char **argv) {
278 if (argc < 1) {
279 wlr_log(WLR_ERROR, "Missing command");
280 return NULL;
281 }
282
283 wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
284 return strdup(argv[0]);
285}
286
287static int parse_timeout(int argc, char **argv) {
288 if (argc < 3) {
289 wlr_log(WLR_ERROR, "Too few parameters to timeout command. "
290 "Usage: timeout <seconds> <command>");
291 exit(-1);
292 }
293 errno = 0;
294 char *endptr;
295 int seconds = strtoul(argv[1], &endptr, 10);
296 if (errno != 0 || *endptr != '\0') {
297 wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a "
298 "numeric value representing seconds", optarg);
299 exit(-1);
300 }
301
302 struct swayidle_timeout_cmd *cmd =
303 calloc(1, sizeof(struct swayidle_timeout_cmd));
304
305 if (seconds > 0) {
306 cmd->timeout = seconds * 1000;
307 } else {
308 cmd->timeout = -1;
309 }
310
311 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
312 wlr_log(WLR_DEBUG, "Setup idle");
313 cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
314
315 int result = 3;
316 if (argc >= 5 && !strcmp("resume", argv[3])) {
317 wlr_log(WLR_DEBUG, "Setup resume");
318 cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
319 result = 5;
320 }
321 list_add(state.timeout_cmds, cmd);
322 return result;
323}
324
325static int parse_sleep(int argc, char **argv) {
326 if (argc < 2) {
327 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
328 "Usage: before-sleep <command>");
329 exit(-1);
330 }
331
332 state.lock_cmd = parse_command(argc - 1, &argv[1]);
333 if (state.lock_cmd) {
334 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", state.lock_cmd);
335 }
336
337 return 2;
338}
339
340static int parse_args(int argc, char *argv[]) {
341 bool debug = false;
342
343 int c;
344 while ((c = getopt(argc, argv, "hd")) != -1) {
345 switch (c) {
346 case 'd':
347 debug = true;
348 break;
349 case 'h':
350 case '?':
351 printf("Usage: %s [OPTIONS]\n", argv[0]);
352 printf(" -d\tdebug\n");
353 printf(" -h\tthis help menu\n");
354 return 1;
355 default:
356 return 1;
357 }
358 }
359
360 wlr_log_init(debug ? WLR_DEBUG : WLR_INFO, NULL);
361
362 state.timeout_cmds = create_list();
363
364 int i = optind;
365 while (i < argc) {
366 if (!strcmp("timeout", argv[i])) {
367 wlr_log(WLR_DEBUG, "Got timeout");
368 i += parse_timeout(argc - i, &argv[i]);
369 } else if (!strcmp("before-sleep", argv[i])) {
370 wlr_log(WLR_DEBUG, "Got before-sleep");
371 i += parse_sleep(argc - i, &argv[i]);
372 } else {
373 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
374 return 1;
375 }
376 }
377
378 return 0;
379}
380
381static int handle_signal(int sig, void *data) {
382 switch (sig) {
383 case SIGINT:
384 case SIGTERM:
385 sway_terminate(0);
386 return 0;
387 case SIGUSR1:
388 wlr_log(WLR_DEBUG, "Got SIGUSR1");
389 for (int i = 0; i < state.timeout_cmds->length; ++i) {
390 register_timeout(state.timeout_cmds->items[i], 0);
391 }
392 return 1;
393 }
394 assert(false); // not reached
395}
396
397static int display_event(int fd, uint32_t mask, void *data) {
398 if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
399 sway_terminate(0);
400 }
401
402 int count = 0;
403 if (mask & WL_EVENT_READABLE) {
404 count = wl_display_dispatch(state.display);
405 }
406 if (mask & WL_EVENT_WRITABLE) {
407 wl_display_flush(state.display);
408 }
409 if (mask == 0) {
410 count = wl_display_dispatch_pending(state.display);
411 wl_display_flush(state.display);
412 }
413
414 if (count < 0) {
415 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
416 sway_terminate(0);
417 }
418
419 return count;
420}
421
422int main(int argc, char *argv[]) {
423 if (parse_args(argc, argv) != 0) {
424 return -1;
425 }
426
427 state.event_loop = wl_event_loop_create();
428
429 wl_event_loop_add_signal(state.event_loop, SIGINT, handle_signal, NULL);
430 wl_event_loop_add_signal(state.event_loop, SIGTERM, handle_signal, NULL);
431 wl_event_loop_add_signal(state.event_loop, SIGUSR1, handle_signal, NULL);
432
433 state.display = wl_display_connect(NULL);
434 if (state.display == NULL) {
435 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
436 "If your compositor is running, check or set the "
437 "WAYLAND_DISPLAY environment variable.");
438 return -3;
439 }
440
441 struct wl_registry *registry = wl_display_get_registry(state.display);
442 wl_registry_add_listener(registry, &registry_listener, NULL);
443 wl_display_roundtrip(state.display);
444
445 if (idle_manager == NULL) {
446 wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
447 return -4;
448 }
449 if (seat == NULL) {
450 wlr_log(WLR_ERROR, "Seat error");
451 return -5;
452 }
453
454 bool should_run = state.timeout_cmds->length > 0;
455#if HAVE_SYSTEMD || HAVE_ELOGIND
456 if (state.lock_cmd) {
457 should_run = true;
458 setup_sleep_listener();
459 }
460#endif
461 if (!should_run) {
462 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
463 sway_terminate(0);
464 }
465
466 for (int i = 0; i < state.timeout_cmds->length; ++i) {
467 struct swayidle_timeout_cmd *cmd = state.timeout_cmds->items[i];
468 register_timeout(cmd, cmd->timeout);
469 }
470
471 wl_display_roundtrip(state.display);
472
473 struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
474 wl_display_get_fd(state.display), WL_EVENT_READABLE,
475 display_event, NULL);
476 wl_event_source_check(source);
477
478 while (wl_event_loop_dispatch(state.event_loop, -1) != 1) {
479 // This space intentionally left blank
480 }
481
482 sway_terminate(0);
483}
diff --git a/swayidle/meson.build b/swayidle/meson.build
deleted file mode 100644
index 79d2c5c4..00000000
--- a/swayidle/meson.build
+++ /dev/null
@@ -1,27 +0,0 @@
1threads = dependency('threads')
2
3swayidle_deps = [
4 client_protos,
5 pixman,
6 wayland_client,
7 wayland_server,
8 wlroots,
9]
10
11if systemd.found()
12 swayidle_deps += systemd
13endif
14if elogind.found()
15 swayidle_deps += elogind
16endif
17
18executable(
19 'swayidle', [
20 'main.c',
21 ],
22 include_directories: [sway_inc],
23 dependencies: swayidle_deps,
24 link_with: [lib_sway_common, lib_sway_client],
25 install_rpath : rpathdir,
26 install: true
27)
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd
deleted file mode 100644
index 0e3b5c3c..00000000
--- a/swayidle/swayidle.1.scd
+++ /dev/null
@@ -1,63 +0,0 @@
1swayidle (1)
2
3# NAME
4
5swayidle - Idle manager for Wayland
6
7# SYNOPSIS
8
9*swayidle* [options] [events...]
10
11# OPTIONS
12
13*-h*
14 Show help message and quit.
15
16*-d*
17 Enable debug output.
18
19# DESCRIPTION
20
21swayidle listens for idle activity on your Wayland compositor and executes tasks
22on various idle-related events. You can specify any number of events at the
23command line.
24
25Sending SIGUSR1 to swayidle will immediately enter idle state.
26
27# EVENTS
28
29*timeout* <timeout> <timeout command> [resume <resume command>]
30 Execute _timeout command_ if there is no activity for <timeout> seconds.
31
32 If you specify "resume <resume command>", _resume command_ will be run when
33 there is activity again.
34
35*before-sleep* <command>
36 If built with systemd support, executes _command_ before systemd puts the
37 computer to sleep.
38
39All commands are executed in a shell.
40
41# EXAMPLE
42
43```
44swayidle \
45 timeout 300 'swaylock -c 000000' \
46 timeout 600 'swaymsg "output * dpms off"' \
47 resume 'swaymsg "output * dpms on"' \
48 before-sleep 'swaylock -c 000000'
49```
50
51This will lock your screen after 300 seconds of inactivity, then turn off your
52displays after another 300 seconds, and turn your screens back on when resumed.
53It will also lock your screen before your computer goes to sleep.
54
55# AUTHORS
56
57Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
58source contributors. For more information about sway development, see
59https://github.com/swaywm/sway.
60
61# SEE ALSO
62
63*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5)
diff --git a/swaylock/main.c b/swaylock/main.c
index 9a4f3b58..0b167da1 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -396,28 +396,34 @@ static void set_default_colors(struct swaylock_colors *colors) {
396 colors->background = 0xFFFFFFFF; 396 colors->background = 0xFFFFFFFF;
397 colors->bs_highlight = 0xDB3300FF; 397 colors->bs_highlight = 0xDB3300FF;
398 colors->key_highlight = 0x33DB00FF; 398 colors->key_highlight = 0x33DB00FF;
399 colors->caps_lock_bs_highlight = 0xDB3300FF;
400 colors->caps_lock_key_highlight = 0x33DB00FF;
399 colors->separator = 0x000000FF; 401 colors->separator = 0x000000FF;
400 colors->inside = (struct swaylock_colorset){ 402 colors->inside = (struct swaylock_colorset){
401 .input = 0x000000C0, 403 .input = 0x000000C0,
402 .cleared = 0xE5A445C0, 404 .cleared = 0xE5A445C0,
405 .caps_lock = 0x000000C0,
403 .verifying = 0x0072FFC0, 406 .verifying = 0x0072FFC0,
404 .wrong = 0xFA0000C0, 407 .wrong = 0xFA0000C0,
405 }; 408 };
406 colors->line = (struct swaylock_colorset){ 409 colors->line = (struct swaylock_colorset){
407 .input = 0x000000FF, 410 .input = 0x000000FF,
408 .cleared = 0x000000FF, 411 .cleared = 0x000000FF,
412 .caps_lock = 0x000000FF,
409 .verifying = 0x000000FF, 413 .verifying = 0x000000FF,
410 .wrong = 0x000000FF, 414 .wrong = 0x000000FF,
411 }; 415 };
412 colors->ring = (struct swaylock_colorset){ 416 colors->ring = (struct swaylock_colorset){
413 .input = 0x337D00FF, 417 .input = 0x337D00FF,
414 .cleared = 0xE5A445FF, 418 .cleared = 0xE5A445FF,
419 .caps_lock = 0xE5A445FF,
415 .verifying = 0x3300FFFF, 420 .verifying = 0x3300FFFF,
416 .wrong = 0x7D3300FF, 421 .wrong = 0x7D3300FF,
417 }; 422 };
418 colors->text = (struct swaylock_colorset){ 423 colors->text = (struct swaylock_colorset){
419 .input = 0xE5A445FF, 424 .input = 0xE5A445FF,
420 .cleared = 0x000000FF, 425 .cleared = 0x000000FF,
426 .caps_lock = 0xE5A445FF,
421 .verifying = 0x000000FF, 427 .verifying = 0x000000FF,
422 .wrong = 0x000000FF, 428 .wrong = 0x000000FF,
423 }; 429 };
@@ -433,25 +439,31 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
433 enum line_mode *line_mode, char **config_path) { 439 enum line_mode *line_mode, char **config_path) {
434 enum long_option_codes { 440 enum long_option_codes {
435 LO_BS_HL_COLOR = 256, 441 LO_BS_HL_COLOR = 256,
442 LO_CAPS_LOCK_BS_HL_COLOR,
443 LO_CAPS_LOCK_KEY_HL_COLOR,
436 LO_FONT, 444 LO_FONT,
437 LO_IND_RADIUS, 445 LO_IND_RADIUS,
438 LO_IND_THICKNESS, 446 LO_IND_THICKNESS,
439 LO_INSIDE_COLOR, 447 LO_INSIDE_COLOR,
440 LO_INSIDE_CLEAR_COLOR, 448 LO_INSIDE_CLEAR_COLOR,
449 LO_INSIDE_CAPS_LOCK_COLOR,
441 LO_INSIDE_VER_COLOR, 450 LO_INSIDE_VER_COLOR,
442 LO_INSIDE_WRONG_COLOR, 451 LO_INSIDE_WRONG_COLOR,
443 LO_KEY_HL_COLOR, 452 LO_KEY_HL_COLOR,
444 LO_LINE_COLOR, 453 LO_LINE_COLOR,
445 LO_LINE_CLEAR_COLOR, 454 LO_LINE_CLEAR_COLOR,
455 LO_LINE_CAPS_LOCK_COLOR,
446 LO_LINE_VER_COLOR, 456 LO_LINE_VER_COLOR,
447 LO_LINE_WRONG_COLOR, 457 LO_LINE_WRONG_COLOR,
448 LO_RING_COLOR, 458 LO_RING_COLOR,
449 LO_RING_CLEAR_COLOR, 459 LO_RING_CLEAR_COLOR,
460 LO_RING_CAPS_LOCK_COLOR,
450 LO_RING_VER_COLOR, 461 LO_RING_VER_COLOR,
451 LO_RING_WRONG_COLOR, 462 LO_RING_WRONG_COLOR,
452 LO_SEP_COLOR, 463 LO_SEP_COLOR,
453 LO_TEXT_COLOR, 464 LO_TEXT_COLOR,
454 LO_TEXT_CLEAR_COLOR, 465 LO_TEXT_CLEAR_COLOR,
466 LO_TEXT_CAPS_LOCK_COLOR,
455 LO_TEXT_VER_COLOR, 467 LO_TEXT_VER_COLOR,
456 LO_TEXT_WRONG_COLOR, 468 LO_TEXT_WRONG_COLOR,
457 }; 469 };
@@ -463,6 +475,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
463 {"daemonize", no_argument, NULL, 'f'}, 475 {"daemonize", no_argument, NULL, 'f'},
464 {"help", no_argument, NULL, 'h'}, 476 {"help", no_argument, NULL, 'h'},
465 {"image", required_argument, NULL, 'i'}, 477 {"image", required_argument, NULL, 'i'},
478 {"disable-caps-lock-text", no_argument, NULL, 'L'},
479 {"indicator-caps-lock", no_argument, NULL, 'l'},
466 {"line-uses-inside", no_argument, NULL, 'n'}, 480 {"line-uses-inside", no_argument, NULL, 'n'},
467 {"socket", required_argument, NULL, 'p'}, 481 {"socket", required_argument, NULL, 'p'},
468 {"line-uses-ring", no_argument, NULL, 'r'}, 482 {"line-uses-ring", no_argument, NULL, 'r'},
@@ -471,25 +485,31 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
471 {"no-unlock-indicator", no_argument, NULL, 'u'}, 485 {"no-unlock-indicator", no_argument, NULL, 'u'},
472 {"version", no_argument, NULL, 'v'}, 486 {"version", no_argument, NULL, 'v'},
473 {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR}, 487 {"bs-hl-color", required_argument, NULL, LO_BS_HL_COLOR},
488 {"caps-lock-bs-hl-color", required_argument, NULL, LO_CAPS_LOCK_BS_HL_COLOR},
489 {"caps-lock-key-hl-color", required_argument, NULL, LO_CAPS_LOCK_KEY_HL_COLOR},
474 {"font", required_argument, NULL, LO_FONT}, 490 {"font", required_argument, NULL, LO_FONT},
475 {"indicator-radius", required_argument, NULL, LO_IND_RADIUS}, 491 {"indicator-radius", required_argument, NULL, LO_IND_RADIUS},
476 {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS}, 492 {"indicator-thickness", required_argument, NULL, LO_IND_THICKNESS},
477 {"inside-color", required_argument, NULL, LO_INSIDE_COLOR}, 493 {"inside-color", required_argument, NULL, LO_INSIDE_COLOR},
478 {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR}, 494 {"inside-clear-color", required_argument, NULL, LO_INSIDE_CLEAR_COLOR},
495 {"inside-caps-lock-color", required_argument, NULL, LO_INSIDE_CAPS_LOCK_COLOR},
479 {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR}, 496 {"inside-ver-color", required_argument, NULL, LO_INSIDE_VER_COLOR},
480 {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR}, 497 {"inside-wrong-color", required_argument, NULL, LO_INSIDE_WRONG_COLOR},
481 {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR}, 498 {"key-hl-color", required_argument, NULL, LO_KEY_HL_COLOR},
482 {"line-color", required_argument, NULL, LO_LINE_COLOR}, 499 {"line-color", required_argument, NULL, LO_LINE_COLOR},
483 {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR}, 500 {"line-clear-color", required_argument, NULL, LO_LINE_CLEAR_COLOR},
501 {"line-caps-lock-color", required_argument, NULL, LO_LINE_CAPS_LOCK_COLOR},
484 {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR}, 502 {"line-ver-color", required_argument, NULL, LO_LINE_VER_COLOR},
485 {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR}, 503 {"line-wrong-color", required_argument, NULL, LO_LINE_WRONG_COLOR},
486 {"ring-color", required_argument, NULL, LO_RING_COLOR}, 504 {"ring-color", required_argument, NULL, LO_RING_COLOR},
487 {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR}, 505 {"ring-clear-color", required_argument, NULL, LO_RING_CLEAR_COLOR},
506 {"ring-caps-lock-color", required_argument, NULL, LO_RING_CAPS_LOCK_COLOR},
488 {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR}, 507 {"ring-ver-color", required_argument, NULL, LO_RING_VER_COLOR},
489 {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR}, 508 {"ring-wrong-color", required_argument, NULL, LO_RING_WRONG_COLOR},
490 {"separator-color", required_argument, NULL, LO_SEP_COLOR}, 509 {"separator-color", required_argument, NULL, LO_SEP_COLOR},
491 {"text-color", required_argument, NULL, LO_TEXT_COLOR}, 510 {"text-color", required_argument, NULL, LO_TEXT_COLOR},
492 {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR}, 511 {"text-clear-color", required_argument, NULL, LO_TEXT_CLEAR_COLOR},
512 {"text-caps-lock-color", required_argument, NULL, LO_TEXT_CAPS_LOCK_COLOR},
493 {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR}, 513 {"text-ver-color", required_argument, NULL, LO_TEXT_VER_COLOR},
494 {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR}, 514 {"text-wrong-color", required_argument, NULL, LO_TEXT_WRONG_COLOR},
495 {0, 0, 0, 0} 515 {0, 0, 0, 0}
@@ -498,76 +518,97 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
498 const char usage[] = 518 const char usage[] =
499 "Usage: swaylock [options...]\n" 519 "Usage: swaylock [options...]\n"
500 "\n" 520 "\n"
501 " -C, --config <config_file> " 521 " -C, --config <config_file> "
502 "Path to the config file.\n" 522 "Path to the config file.\n"
503 " -c, --color <color> " 523 " -c, --color <color> "
504 "Turn the screen into the given color instead of white.\n" 524 "Turn the screen into the given color instead of white.\n"
505 " -e, --ignore-empty-password " 525 " -e, --ignore-empty-password "
506 "When an empty password is provided, do not validate it.\n" 526 "When an empty password is provided, do not validate it.\n"
507 " -f, --daemonize " 527 " -f, --daemonize "
508 "Detach from the controlling terminal after locking.\n" 528 "Detach from the controlling terminal after locking.\n"
509 " -h, --help " 529 " -h, --help "
510 "Show help message and quit.\n" 530 "Show help message and quit.\n"
511 " -i, --image [<output>:]<path> " 531 " -i, --image [<output>:]<path> "
512 "Display the given image.\n" 532 "Display the given image.\n"
513 " -s, --scaling <mode> " 533 " -L, --disable-caps-lock-text "
534 "Disable the Caps Lock text.\n"
535 " -l, --indicator-caps-lock "
536 "Show the current Caps Lock state also on the indicator.\n"
537 " -s, --scaling <mode> "
514 "Scaling mode: stretch, fill, fit, center, tile.\n" 538 "Scaling mode: stretch, fill, fit, center, tile.\n"
515 " -t, --tiling " 539 " -t, --tiling "
516 "Same as --scaling=tile.\n" 540 "Same as --scaling=tile.\n"
517 " -u, --no-unlock-indicator " 541 " -u, --no-unlock-indicator "
518 "Disable the unlock indicator.\n" 542 "Disable the unlock indicator.\n"
519 " -v, --version " 543 " -v, --version "
520 "Show the version number and quit.\n" 544 "Show the version number and quit.\n"
521 " --bs-hl-color <color> " 545 " --bs-hl-color <color> "
522 "Sets the color of backspace highlight segments.\n" 546 "Sets the color of backspace highlight segments.\n"
523 " --font <font> " 547 " --caps-lock-bs-hl-color <color> "
548 "Sets the color of backspace highlight segments when Caps Lock "
549 "is active.\n"
550 " --caps-lock-key-hl-color <color> "
551 "Sets the color of the key press highlight segments when "
552 "Caps Lock is active.\n"
553 " --font <font> "
524 "Sets the font of the text.\n" 554 "Sets the font of the text.\n"
525 " --indicator-radius <radius> " 555 " --indicator-radius <radius> "
526 "Sets the indicator radius.\n" 556 "Sets the indicator radius.\n"
527 " --indicator-thickness <thick> " 557 " --indicator-thickness <thick> "
528 "Sets the indicator thickness.\n" 558 "Sets the indicator thickness.\n"
529 " --inside-color <color> " 559 " --inside-color <color> "
530 "Sets the color of the inside of the indicator.\n" 560 "Sets the color of the inside of the indicator.\n"
531 " --inside-clear-color <color> " 561 " --inside-clear-color <color> "
532 "Sets the color of the inside of the indicator when cleared.\n" 562 "Sets the color of the inside of the indicator when cleared.\n"
533 " --inside-ver-color <color> " 563 " --inside-caps-lock-color <color> "
564 "Sets the color of the inside of the indicator when Caps Lock "
565 "is active.\n"
566 " --inside-ver-color <color> "
534 "Sets the color of the inside of the indicator when verifying.\n" 567 "Sets the color of the inside of the indicator when verifying.\n"
535 " --inside-wrong-color <color> " 568 " --inside-wrong-color <color> "
536 "Sets the color of the inside of the indicator when invalid.\n" 569 "Sets the color of the inside of the indicator when invalid.\n"
537 " --key-hl-color <color> " 570 " --key-hl-color <color> "
538 "Sets the color of the key press highlight segments.\n" 571 "Sets the color of the key press highlight segments.\n"
539 " --line-color <color> " 572 " --line-color <color> "
540 "Sets the color of the line between the inside and ring.\n" 573 "Sets the color of the line between the inside and ring.\n"
541 " --line-clear-color <color> " 574 " --line-clear-color <color> "
542 "Sets the color of the line between the inside and ring when " 575 "Sets the color of the line between the inside and ring when "
543 "cleared.\n" 576 "cleared.\n"
544 " --line-ver-color <color> " 577 " --line-caps-lock-color <color> "
578 "Sets the color of the line between the inside and ring when "
579 "Caps Lock is active.\n"
580 " --line-ver-color <color> "
545 "Sets the color of the line between the inside and ring when " 581 "Sets the color of the line between the inside and ring when "
546 "verifying.\n" 582 "verifying.\n"
547 " --line-wrong-color <color> " 583 " --line-wrong-color <color> "
548 "Sets the color of the line between the inside and ring when " 584 "Sets the color of the line between the inside and ring when "
549 "invalid.\n" 585 "invalid.\n"
550 " -n, --line-uses-inside " 586 " -n, --line-uses-inside "
551 "Use the inside color for the line between the inside and ring.\n" 587 "Use the inside color for the line between the inside and ring.\n"
552 " -r, --line-uses-ring " 588 " -r, --line-uses-ring "
553 "Use the ring color for the line between the inside and ring.\n" 589 "Use the ring color for the line between the inside and ring.\n"
554 " --ring-color <color> " 590 " --ring-color <color> "
555 "Sets the color of the ring of the indicator.\n" 591 "Sets the color of the ring of the indicator.\n"
556 " --ring-clear-color <color> " 592 " --ring-clear-color <color> "
557 "Sets the color of the ring of the indicator when cleared.\n" 593 "Sets the color of the ring of the indicator when cleared.\n"
558 " --ring-ver-color <color> " 594 " --ring-caps-lock-color <color> "
595 "Sets the color of the ring of the indicator when Caps Lock "
596 "is active.\n"
597 " --ring-ver-color <color> "
559 "Sets the color of the ring of the indicator when verifying.\n" 598 "Sets the color of the ring of the indicator when verifying.\n"
560 " --ring-wrong-color <color> " 599 " --ring-wrong-color <color> "
561 "Sets the color of the ring of the indicator when invalid.\n" 600 "Sets the color of the ring of the indicator when invalid.\n"
562 " --separator-color <color> " 601 " --separator-color <color> "
563 "Sets the color of the lines that separate highlight segments.\n" 602 "Sets the color of the lines that separate highlight segments.\n"
564 " --text-color <color> " 603 " --text-color <color> "
565 "Sets the color of the text.\n" 604 "Sets the color of the text.\n"
566 " --text-clear-color <color> " 605 " --text-clear-color <color> "
567 "Sets the color of the text when cleared.\n" 606 "Sets the color of the text when cleared.\n"
568 " --text-ver-color <color> " 607 " --text-caps-lock-color <color> "
608 "Sets the color of the text when Caps Lock is active.\n"
609 " --text-ver-color <color> "
569 "Sets the color of the text when verifying.\n" 610 "Sets the color of the text when verifying.\n"
570 " --text-wrong-color <color> " 611 " --text-wrong-color <color> "
571 "Sets the color of the text when invalid.\n" 612 "Sets the color of the text when invalid.\n"
572 "\n" 613 "\n"
573 "All <color> options are of the form <rrggbb[aa]>.\n"; 614 "All <color> options are of the form <rrggbb[aa]>.\n";
@@ -576,7 +617,7 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
576 optind = 1; 617 optind = 1;
577 while (1) { 618 while (1) {
578 int opt_idx = 0; 619 int opt_idx = 0;
579 c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); 620 c = getopt_long(argc, argv, "c:efhi:Llnrs:tuvC:", long_options, &opt_idx);
580 if (c == -1) { 621 if (c == -1) {
581 break; 622 break;
582 } 623 }
@@ -607,6 +648,16 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
607 load_image(optarg, state); 648 load_image(optarg, state);
608 } 649 }
609 break; 650 break;
651 case 'L':
652 if (state) {
653 state->args.show_caps_lock_text = false;
654 }
655 break;
656 case 'l':
657 if (state) {
658 state->args.show_caps_lock_indicator = true;
659 }
660 break;
610 case 'n': 661 case 'n':
611 if (line_mode) { 662 if (line_mode) {
612 *line_mode = LM_INSIDE; 663 *line_mode = LM_INSIDE;
@@ -644,6 +695,16 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
644 state->args.colors.bs_highlight = parse_color(optarg); 695 state->args.colors.bs_highlight = parse_color(optarg);
645 } 696 }
646 break; 697 break;
698 case LO_CAPS_LOCK_BS_HL_COLOR:
699 if (state) {
700 state->args.colors.caps_lock_bs_highlight = parse_color(optarg);
701 }
702 break;
703 case LO_CAPS_LOCK_KEY_HL_COLOR:
704 if (state) {
705 state->args.colors.caps_lock_key_highlight = parse_color(optarg);
706 }
707 break;
647 case LO_FONT: 708 case LO_FONT:
648 if (state) { 709 if (state) {
649 free(state->args.font); 710 free(state->args.font);
@@ -670,6 +731,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
670 state->args.colors.inside.cleared = parse_color(optarg); 731 state->args.colors.inside.cleared = parse_color(optarg);
671 } 732 }
672 break; 733 break;
734 case LO_INSIDE_CAPS_LOCK_COLOR:
735 if (state) {
736 state->args.colors.inside.caps_lock = parse_color(optarg);
737 }
738 break;
673 case LO_INSIDE_VER_COLOR: 739 case LO_INSIDE_VER_COLOR:
674 if (state) { 740 if (state) {
675 state->args.colors.inside.verifying = parse_color(optarg); 741 state->args.colors.inside.verifying = parse_color(optarg);
@@ -695,6 +761,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
695 state->args.colors.line.cleared = parse_color(optarg); 761 state->args.colors.line.cleared = parse_color(optarg);
696 } 762 }
697 break; 763 break;
764 case LO_LINE_CAPS_LOCK_COLOR:
765 if (state) {
766 state->args.colors.line.caps_lock = parse_color(optarg);
767 }
768 break;
698 case LO_LINE_VER_COLOR: 769 case LO_LINE_VER_COLOR:
699 if (state) { 770 if (state) {
700 state->args.colors.line.verifying = parse_color(optarg); 771 state->args.colors.line.verifying = parse_color(optarg);
@@ -715,6 +786,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
715 state->args.colors.ring.cleared = parse_color(optarg); 786 state->args.colors.ring.cleared = parse_color(optarg);
716 } 787 }
717 break; 788 break;
789 case LO_RING_CAPS_LOCK_COLOR:
790 if (state) {
791 state->args.colors.ring.caps_lock = parse_color(optarg);
792 }
793 break;
718 case LO_RING_VER_COLOR: 794 case LO_RING_VER_COLOR:
719 if (state) { 795 if (state) {
720 state->args.colors.ring.verifying = parse_color(optarg); 796 state->args.colors.ring.verifying = parse_color(optarg);
@@ -740,6 +816,11 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
740 state->args.colors.text.cleared = parse_color(optarg); 816 state->args.colors.text.cleared = parse_color(optarg);
741 } 817 }
742 break; 818 break;
819 case LO_TEXT_CAPS_LOCK_COLOR:
820 if (state) {
821 state->args.colors.text.caps_lock = parse_color(optarg);
822 }
823 break;
743 case LO_TEXT_VER_COLOR: 824 case LO_TEXT_VER_COLOR:
744 if (state) { 825 if (state) {
745 state->args.colors.text.verifying = parse_color(optarg); 826 state->args.colors.text.verifying = parse_color(optarg);
@@ -857,6 +938,8 @@ int main(int argc, char **argv) {
857 .thickness = 10, 938 .thickness = 10,
858 .ignore_empty = false, 939 .ignore_empty = false,
859 .show_indicator = true, 940 .show_indicator = true,
941 .show_caps_lock_indicator = false,
942 .show_caps_lock_text = true
860 }; 943 };
861 wl_list_init(&state.images); 944 wl_list_init(&state.images);
862 set_default_colors(&state.args.colors); 945 set_default_colors(&state.args.colors);
diff --git a/swaylock/render.c b/swaylock/render.c
index cbd5d01d..5aedaad5 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -18,7 +18,17 @@ static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state,
18 } else if (state->auth_state == AUTH_STATE_CLEAR) { 18 } else if (state->auth_state == AUTH_STATE_CLEAR) {
19 cairo_set_source_u32(cairo, colorset->cleared); 19 cairo_set_source_u32(cairo, colorset->cleared);
20 } else { 20 } else {
21 cairo_set_source_u32(cairo, colorset->input); 21 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
22 cairo_set_source_u32(cairo, colorset->caps_lock);
23 } else if (state->xkb.caps_lock && !state->args.show_caps_lock_indicator &&
24 state->args.show_caps_lock_text) {
25 uint32_t inputtextcolor = state->args.colors.text.input;
26 state->args.colors.text.input = state->args.colors.text.caps_lock;
27 cairo_set_source_u32(cairo, colorset->input);
28 state->args.colors.text.input = inputtextcolor;
29 } else {
30 cairo_set_source_u32(cairo, colorset->input);
31 }
22 } 32 }
23} 33}
24 34
@@ -92,7 +102,8 @@ void render_frame(struct swaylock_surface *surface) {
92 break; 102 break;
93 case AUTH_STATE_INPUT: 103 case AUTH_STATE_INPUT:
94 case AUTH_STATE_INPUT_NOP: 104 case AUTH_STATE_INPUT_NOP:
95 if (state->xkb.caps_lock) { 105 case AUTH_STATE_BACKSPACE:
106 if (state->xkb.caps_lock && state->args.show_caps_lock_text) {
96 text = "Caps Lock"; 107 text = "Caps Lock";
97 } 108 }
98 break; 109 break;
@@ -125,9 +136,17 @@ void render_frame(struct swaylock_surface *surface) {
125 arc_radius, highlight_start, 136 arc_radius, highlight_start,
126 highlight_start + TYPE_INDICATOR_RANGE); 137 highlight_start + TYPE_INDICATOR_RANGE);
127 if (state->auth_state == AUTH_STATE_INPUT) { 138 if (state->auth_state == AUTH_STATE_INPUT) {
128 cairo_set_source_u32(cairo, state->args.colors.key_highlight); 139 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
140 cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight);
141 } else {
142 cairo_set_source_u32(cairo, state->args.colors.key_highlight);
143 }
129 } else { 144 } else {
130 cairo_set_source_u32(cairo, state->args.colors.bs_highlight); 145 if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) {
146 cairo_set_source_u32(cairo, state->args.colors.caps_lock_bs_highlight);
147 } else {
148 cairo_set_source_u32(cairo, state->args.colors.bs_highlight);
149 }
131 } 150 }
132 cairo_stroke(cairo); 151 cairo_stroke(cairo);
133 152
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd
index 9f39a913..2c7979be 100644
--- a/swaylock/swaylock.1.scd
+++ b/swaylock/swaylock.1.scd
@@ -44,6 +44,12 @@ Locks your Wayland session.
44 Display the given image, optionally only on the given output. Use -c to set 44 Display the given image, optionally only on the given output. Use -c to set
45 a background color. 45 a background color.
46 46
47*-L, --disable-caps-lock-text*
48 Disable the Caps Lock Text.
49
50*-l, --indicator-caps-lock*
51 Show the current Caps Lock state also on the indicator.
52
47*-s, --scaling* 53*-s, --scaling*
48 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. 54 Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_.
49 55
@@ -58,6 +64,12 @@ Locks your Wayland session.
58*--bs-hl-color* <rrggbb[aa]> 64*--bs-hl-color* <rrggbb[aa]>
59 Sets the color of backspace highlight segments. 65 Sets the color of backspace highlight segments.
60 66
67*--caps-lock-bs-hl-color* <rrggbb[aa]>
68 Sets the color of backspace highlight segments when Caps Lock is active.
69
70*--caps-lock-bs-hl-color* <rrggbb[aa]>
71 Sets the color of the key press highlight segments when Caps Lock is active.
72
61*--font* <font> 73*--font* <font>
62 Sets the font of the text inside the indicator. 74 Sets the font of the text inside the indicator.
63 75
@@ -75,6 +87,9 @@ Locks your Wayland session.
75*--inside-clear-color* <rrggbb[aa]> 87*--inside-clear-color* <rrggbb[aa]>
76 Sets the color of the inside of the indicator when cleared. 88 Sets the color of the inside of the indicator when cleared.
77 89
90*--inside-caps-lock-color* <rrggbb[aa]>
91 Sets the color of the inside of the indicator when Caps Lock is active.
92
78*--inside-ver-color* <rrggbb[aa]> 93*--inside-ver-color* <rrggbb[aa]>
79 Sets the color of the inside of the indicator when verifying. 94 Sets the color of the inside of the indicator when verifying.
80 95
@@ -92,6 +107,10 @@ Locks your Wayland session.
92 Sets the color of the lines that separate the inside and outside of the 107 Sets the color of the lines that separate the inside and outside of the
93 indicator when cleared. 108 indicator when cleared.
94 109
110*--line-caps-lock-color* <rrggbb[aa]>
111 Sets the color of the line between the inside and ring when Caps Lock
112 is active.
113
95*--line-ver-color* <rrggbb[aa]> 114*--line-ver-color* <rrggbb[aa]>
96 Sets the color of the lines that separate the inside and outside of the 115 Sets the color of the lines that separate the inside and outside of the
97 indicator when verifying. 116 indicator when verifying.
@@ -114,6 +133,9 @@ Locks your Wayland session.
114*--ring-clear-color* <rrggbb[aa]> 133*--ring-clear-color* <rrggbb[aa]>
115 Sets the color of the outside of the indicator when cleared. 134 Sets the color of the outside of the indicator when cleared.
116 135
136*--ring-caps-lock-color* <rrggbb[aa]>
137 Sets the color of the ring of the indicator when Caps Lock is active.
138
117*--ring-ver-color* <rrggbb[aa]> 139*--ring-ver-color* <rrggbb[aa]>
118 Sets the color of the outside of the indicator when verifying. 140 Sets the color of the outside of the indicator when verifying.
119 141
@@ -129,6 +151,9 @@ Locks your Wayland session.
129*--text-clear-color* <rrggbb[aa]> 151*--text-clear-color* <rrggbb[aa]>
130 Sets the color of the text inside the indicator when cleared. 152 Sets the color of the text inside the indicator when cleared.
131 153
154*--text-caps-lock-color* <rrggbb[aa]>
155 Sets the color of the text when Caps Lock is active.
156
132*--text-ver-color* <rrggbb[aa]> 157*--text-ver-color* <rrggbb[aa]>
133 Sets the color of the text inside the indicator when verifying. 158 Sets the color of the text inside the indicator when verifying.
134 159
diff --git a/swaymsg/main.c b/swaymsg/main.c
index f1bb5e3e..e5eee631 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -113,7 +113,7 @@ static const char *pretty_type_name(const char *name) {
113} 113}
114 114
115static void pretty_print_input(json_object *i) { 115static void pretty_print_input(json_object *i) {
116 json_object *id, *name, *type, *product, *vendor, *kbdlayout; 116 json_object *id, *name, *type, *product, *vendor, *kbdlayout, *events;
117 json_object_object_get_ex(i, "identifier", &id); 117 json_object_object_get_ex(i, "identifier", &id);
118 json_object_object_get_ex(i, "name", &name); 118 json_object_object_get_ex(i, "name", &name);
119 json_object_object_get_ex(i, "type", &type); 119 json_object_object_get_ex(i, "type", &type);
@@ -139,6 +139,10 @@ static void pretty_print_input(json_object *i) {
139 json_object_get_string(kbdlayout)); 139 json_object_get_string(kbdlayout));
140 } 140 }
141 141
142 if (json_object_object_get_ex(i, "libinput_send_events", &events)) {
143 printf(" Libinput Send Events: %s\n", json_object_get_string(events));
144 }
145
142 printf("\n"); 146 printf("\n");
143} 147}
144 148