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.h5
-rw-r--r--include/sway/input/cursor.h8
-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/bind.c126
-rw-r--r--sway/commands/input/events.c76
-rw-r--r--sway/config.c21
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/input/cursor.c73
-rw-r--r--sway/ipc-json.c25
-rw-r--r--sway/ipc-server.c8
-rw-r--r--sway/meson.build2
-rw-r--r--sway/sway-bar.5.scd12
-rw-r--r--sway/sway-input.5.scd9
-rw-r--r--sway/sway.5.scd2
-rw-r--r--sway/tree/output.c2
-rw-r--r--swaybar/bar.c30
-rw-r--r--swaybar/config.c2
-rw-r--r--swaybar/ipc.c11
-rw-r--r--swaybar/render.c117
-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
37 files changed, 634 insertions, 822 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 ebf16e6a..a667984d 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -27,7 +27,8 @@ struct sway_variable {
27enum binding_input_type { 27enum binding_input_type {
28 BINDING_KEYCODE, 28 BINDING_KEYCODE,
29 BINDING_KEYSYM, 29 BINDING_KEYSYM,
30 BINDING_MOUSE, 30 BINDING_MOUSECODE,
31 BINDING_MOUSESYM,
31}; 32};
32 33
33enum binding_flags { 34enum binding_flags {
@@ -231,6 +232,8 @@ struct bar_config {
231 bool verbose; 232 bool verbose;
232 struct side_gaps gaps; 233 struct side_gaps gaps;
233 pid_t pid; 234 pid_t pid;
235 int status_padding;
236 int status_edge_padding;
234 struct { 237 struct {
235 char *background; 238 char *background;
236 char *statusline; 239 char *statusline;
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 22e278b0..4636bf6b 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -86,4 +86,12 @@ void cursor_warp_to_container(struct sway_cursor *cursor,
86 86
87void cursor_warp_to_workspace(struct sway_cursor *cursor, 87void cursor_warp_to_workspace(struct sway_cursor *cursor,
88 struct sway_workspace *workspace); 88 struct sway_workspace *workspace);
89
90uint32_t get_mouse_bindsym(const char *name, char **error);
91
92uint32_t get_mouse_bindcode(const char *name, char **error);
93
94// Considers both bindsym and bindcode
95uint32_t get_mouse_button(const char *name, char **error);
96
89#endif 97#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/bind.c b/sway/commands/bind.c
index 4fceaf8c..be47d412 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -85,69 +85,91 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) {
85 */ 85 */
86static struct cmd_results *identify_key(const char* name, bool first_key, 86static struct cmd_results *identify_key(const char* name, bool first_key,
87 uint32_t* key_val, enum binding_input_type* type) { 87 uint32_t* key_val, enum binding_input_type* type) {
88 if (*type == BINDING_KEYCODE) { 88 if (*type == BINDING_MOUSECODE) {
89 // check for keycode 89 // check for mouse bindcodes
90 xkb_keycode_t keycode = strtol(name, NULL, 10); 90 char *message = NULL;
91 if (!xkb_keycode_is_legal_ext(keycode)) { 91 uint32_t button = get_mouse_bindcode(name, &message);
92 return cmd_results_new(CMD_INVALID, "bindcode", 92 if (!button) {
93 "Invalid keycode '%s'", name); 93 if (message) {
94 struct cmd_results *error =
95 cmd_results_new(CMD_INVALID, "bindcode", message);
96 free(message);
97 return error;
98 } else {
99 return cmd_results_new(CMD_INVALID, "bindcode",
100 "Unknown button code %s", name);
101 }
94 } 102 }
95 *key_val = keycode; 103 *key_val = button;
96 } else { 104 } else if (*type == BINDING_MOUSESYM) {
97 // check for keysym 105 // check for mouse bindsyms (x11 buttons or event names)
98 xkb_keysym_t keysym = xkb_keysym_from_name(name, 106 char *message = NULL;
99 XKB_KEYSYM_CASE_INSENSITIVE); 107 uint32_t button = get_mouse_bindsym(name, &message);
100 108 if (!button) {
101 // Check for mouse binding 109 if (message) {
102 uint32_t button = 0; 110 struct cmd_results *error =
103 if (strncasecmp(name, "button", strlen("button")) == 0) { 111 cmd_results_new(CMD_INVALID, "bindsym", message);
104 // Map to x11 mouse buttons 112 free(message);
105 button = name[strlen("button")] - '0'; 113 return error;
106 if (button < 1 || button > 9 || strlen(name) > strlen("button0")) { 114 } else if (!button) {
107 return cmd_results_new(CMD_INVALID, "bindsym", 115 return cmd_results_new(CMD_INVALID, "bindsym",
108 "Only buttons 1-9 are supported. For other mouse " 116 "Unknown button %s", name);
109 "buttons, use the name of the event code.");
110 } 117 }
111 uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, 118 }
112 SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, 119 *key_val = button;
113 SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; 120 } else if (*type == BINDING_KEYCODE) {
114 button = buttons[button - 1]; 121 // check for keycode. If it is the first key, allow mouse bindcodes
115 } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { 122 if (first_key) {
116 // Get event code 123 char *message = NULL;
117 int code = libevdev_event_code_from_name(EV_KEY, name); 124 uint32_t button = get_mouse_bindcode(name, &message);
118 if (code == -1) { 125 free(message);
119 return cmd_results_new(CMD_INVALID, "bindsym", 126 if (button) {
120 "Invalid event code name %s", name); 127 *type = BINDING_MOUSECODE;
128 *key_val = button;
129 return NULL;
121 } 130 }
122 button = code;
123 } 131 }
124 132
125 if (*type == BINDING_KEYSYM) { 133 xkb_keycode_t keycode = strtol(name, NULL, 10);
126 if (button) { 134 if (!xkb_keycode_is_legal_ext(keycode)) {
127 if (first_key) { 135 if (first_key) {
128 *type = BINDING_MOUSE; 136 return cmd_results_new(CMD_INVALID, "bindcode",
129 *key_val = button; 137 "Invalid keycode or button code '%s'", name);
130 } else {
131 return cmd_results_new(CMD_INVALID, "bindsym",
132 "Mixed button '%s' into key sequence", name);
133 }
134 } else if (keysym) {
135 *key_val = keysym;
136 } else { 138 } else {
137 return cmd_results_new(CMD_INVALID, "bindsym", 139 return cmd_results_new(CMD_INVALID, "bindcode",
138 "Unknown key '%s'", name); 140 "Invalid keycode '%s'", name);
139 } 141 }
140 } else { 142 }
141 if (button) { 143 *key_val = keycode;
144 } else {
145 // check for keysym. If it is the first key, allow mouse bindsyms
146 if (first_key) {
147 char *message = NULL;
148 uint32_t button = get_mouse_bindsym(name, &message);
149 if (message) {
150 struct cmd_results *error =
151 cmd_results_new(CMD_INVALID, "bindsym", message);
152 free(message);
153 return error;
154 } else if (button) {
155 *type = BINDING_MOUSESYM;
142 *key_val = button; 156 *key_val = button;
143 } else if (keysym) { 157 return NULL;
158 }
159 }
160
161 xkb_keysym_t keysym = xkb_keysym_from_name(name,
162 XKB_KEYSYM_CASE_INSENSITIVE);
163 if (!keysym) {
164 if (first_key) {
144 return cmd_results_new(CMD_INVALID, "bindsym", 165 return cmd_results_new(CMD_INVALID, "bindsym",
145 "Mixed keysym '%s' into button sequence", name); 166 "Unknown key or button '%s'", name);
146 } else { 167 } else {
147 return cmd_results_new(CMD_INVALID, "bindsym", 168 return cmd_results_new(CMD_INVALID, "bindsym",
148 "Unknown button '%s'", name); 169 "Unknown key '%s'", name);
149 } 170 }
150 } 171 }
172 *key_val = keysym;
151 } 173 }
152 return NULL; 174 return NULL;
153} 175}
@@ -201,7 +223,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
201 } 223 }
202 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) 224 if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR)
203 || exclude_titlebar) { 225 || exclude_titlebar) {
204 binding->type = BINDING_MOUSE; 226 binding->type = binding->type == BINDING_KEYCODE ?
227 BINDING_MOUSECODE : BINDING_MOUSESYM;
205 } 228 }
206 229
207 if (argc < 2) { 230 if (argc < 2) {
@@ -249,7 +272,8 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
249 // that this is one 272 // that this is one
250 if (exclude_titlebar) { 273 if (exclude_titlebar) {
251 binding->flags &= ~BINDING_TITLEBAR; 274 binding->flags &= ~BINDING_TITLEBAR;
252 } else if (binding->type == BINDING_MOUSE) { 275 } else if (binding->type == BINDING_MOUSECODE
276 || binding->type == BINDING_MOUSESYM) {
253 binding->flags |= BINDING_TITLEBAR; 277 binding->flags |= BINDING_TITLEBAR;
254 } 278 }
255 279
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/config.c b/sway/config.c
index cd0857f4..5ca4806c 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -573,15 +573,18 @@ bool load_include_configs(const char *path, struct sway_config *config,
573} 573}
574 574
575// get line, with backslash continuation 575// get line, with backslash continuation
576static 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) {
577 char *next_line = NULL; 578 char *next_line = NULL;
578 size_t next_line_size = 0; 579 size_t next_line_size = 0;
579 ssize_t nread = getline(lineptr, line_size, file); 580 ssize_t nread = getline(lineptr, line_size, file);
581 *nlines = nread == -1 ? 0 : 1;
580 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) { 582 while (nread >= 2 && strcmp(&(*lineptr)[nread - 2], "\\\n") == 0) {
581 ssize_t next_nread = getline(&next_line, &next_line_size, file); 583 ssize_t next_nread = getline(&next_line, &next_line_size, file);
582 if (next_nread == -1) { 584 if (next_nread == -1) {
583 break; 585 break;
584 } 586 }
587 (*nlines)++;
585 588
586 nread += next_nread - 2; 589 nread += next_nread - 2;
587 if ((ssize_t) *line_size < nread + 1) { 590 if ((ssize_t) *line_size < nread + 1) {
@@ -599,6 +602,7 @@ static ssize_t getline_with_cont(char **lineptr, size_t *line_size, FILE *file)
599} 602}
600 603
601static int detect_brace(FILE *file) { 604static int detect_brace(FILE *file) {
605 int ret = 0;
602 int lines = 0; 606 int lines = 0;
603 long pos = ftell(file); 607 long pos = ftell(file);
604 char *line = NULL; 608 char *line = NULL;
@@ -607,15 +611,17 @@ static int detect_brace(FILE *file) {
607 lines++; 611 lines++;
608 strip_whitespace(line); 612 strip_whitespace(line);
609 if (*line) { 613 if (*line) {
610 if (strcmp(line, "{") != 0) { 614 if (strcmp(line, "{") == 0) {
611 fseek(file, pos, SEEK_SET); 615 ret = lines;
612 lines = 0;
613 } 616 }
614 break; 617 break;
615 } 618 }
616 } 619 }
617 free(line); 620 free(line);
618 return lines; 621 if (ret == 0) {
622 fseek(file, pos, SEEK_SET);
623 }
624 return ret;
619} 625}
620 626
621static char *expand_line(const char *block, const char *line, bool add_brace) { 627static char *expand_line(const char *block, const char *line, bool add_brace) {
@@ -662,7 +668,8 @@ bool read_config(FILE *file, struct sway_config *config,
662 ssize_t nread; 668 ssize_t nread;
663 list_t *stack = create_list(); 669 list_t *stack = create_list();
664 size_t read = 0; 670 size_t read = 0;
665 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) {
666 if (reading_main_config) { 673 if (reading_main_config) {
667 if (read + nread > config_size) { 674 if (read + nread > config_size) {
668 wlr_log(WLR_ERROR, "Config file changed during reading"); 675 wlr_log(WLR_ERROR, "Config file changed during reading");
@@ -678,7 +685,7 @@ bool read_config(FILE *file, struct sway_config *config,
678 line[nread - 1] = '\0'; 685 line[nread - 1] = '\0';
679 } 686 }
680 687
681 line_number++; 688 line_number += nlines;
682 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); 689 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
683 690
684 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/input/cursor.c b/sway/input/cursor.c
index 96feb47d..9af7ef57 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -2,8 +2,10 @@
2#include <math.h> 2#include <math.h>
3#include <libevdev/libevdev.h> 3#include <libevdev/libevdev.h>
4#include <linux/input-event-codes.h> 4#include <linux/input-event-codes.h>
5#include <errno.h>
5#include <float.h> 6#include <float.h>
6#include <limits.h> 7#include <limits.h>
8#include <strings.h>
7#include <wlr/types/wlr_cursor.h> 9#include <wlr/types/wlr_cursor.h>
8#include <wlr/types/wlr_xcursor_manager.h> 10#include <wlr/types/wlr_xcursor_manager.h>
9#include <wlr/types/wlr_idle.h> 11#include <wlr/types/wlr_idle.h>
@@ -85,6 +87,10 @@ static struct sway_node *node_at_coords(
85 return NULL; 87 return NULL;
86 } 88 }
87 struct sway_output *output = wlr_output->data; 89 struct sway_output *output = wlr_output->data;
90 if (!output) {
91 // output is being destroyed
92 return NULL;
93 }
88 double ox = lx, oy = ly; 94 double ox = lx, oy = ly;
89 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); 95 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
90 96
@@ -1092,6 +1098,8 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1092 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; 1098 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
1093 bool on_border = edge != WLR_EDGE_NONE; 1099 bool on_border = edge != WLR_EDGE_NONE;
1094 bool on_titlebar = cont && !on_border && !surface; 1100 bool on_titlebar = cont && !on_border && !surface;
1101 bool on_titlebar_border = cont && on_border &&
1102 cursor->cursor->y < cont->content_y;
1095 bool on_contents = cont && !on_border && surface; 1103 bool on_contents = cont && !on_border && surface;
1096 float scroll_factor = 1104 float scroll_factor =
1097 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; 1105 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
@@ -1117,7 +1125,7 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1117 } 1125 }
1118 1126
1119 // Scrolling on a tabbed or stacked title bar (handled as press event) 1127 // Scrolling on a tabbed or stacked title bar (handled as press event)
1120 if (!handled && on_titlebar) { 1128 if (!handled && (on_titlebar || on_titlebar_border)) {
1121 enum sway_container_layout layout = container_parent_layout(cont); 1129 enum sway_container_layout layout = container_parent_layout(cont);
1122 if (layout == L_TABBED || layout == L_STACKED) { 1130 if (layout == L_TABBED || layout == L_STACKED) {
1123 struct sway_node *tabcontainer = node_get_parent(node); 1131 struct sway_node *tabcontainer = node_get_parent(node);
@@ -1527,3 +1535,66 @@ void cursor_warp_to_workspace(struct sway_cursor *cursor,
1527 1535
1528 wlr_cursor_warp(cursor->cursor, NULL, x, y); 1536 wlr_cursor_warp(cursor->cursor, NULL, x, y);
1529} 1537}
1538
1539uint32_t get_mouse_bindsym(const char *name, char **error) {
1540 if (strncasecmp(name, "button", strlen("button")) == 0) {
1541 // Map to x11 mouse buttons
1542 int number = name[strlen("button")] - '0';
1543 if (number < 1 || number > 9 || strlen(name) > strlen("button0")) {
1544 *error = strdup("Only buttons 1-9 are supported. For other mouse "
1545 "buttons, use the name of the event code.");
1546 return 0;
1547 }
1548 static const uint32_t buttons[] = {BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
1549 SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
1550 SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
1551 return buttons[number - 1];
1552 } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
1553 // Get event code from name
1554 int code = libevdev_event_code_from_name(EV_KEY, name);
1555 if (code == -1) {
1556 size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1;
1557 *error = malloc(len);
1558 if (*error) {
1559 snprintf(*error, len, "Unknown event %s", name);
1560 }
1561 return 0;
1562 }
1563 return code;
1564 }
1565 return 0;
1566}
1567
1568uint32_t get_mouse_bindcode(const char *name, char **error) {
1569 // Validate event code
1570 errno = 0;
1571 char *endptr;
1572 int code = strtol(name, &endptr, 10);
1573 if (endptr == name && code <= 0) {
1574 *error = strdup("Button event code must be a positive integer.");
1575 return 0;
1576 } else if (errno == ERANGE) {
1577 *error = strdup("Button event code out of range.");
1578 return 0;
1579 }
1580 const char *event = libevdev_event_code_get_name(EV_KEY, code);
1581 if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
1582 size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button",
1583 code, event) + 1;
1584 *error = malloc(len);
1585 if (*error) {
1586 snprintf(*error, len, "Event code %d (%s) is not a button",
1587 code, event);
1588 }
1589 return 0;
1590 }
1591 return code;
1592}
1593
1594uint32_t get_mouse_button(const char *name, char **error) {
1595 uint32_t button = get_mouse_bindsym(name, error);
1596 if (!button && !error) {
1597 button = get_mouse_bindcode(name, error);
1598 }
1599 return button;
1600}
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/ipc-server.c b/sway/ipc-server.c
index 456db866..ff1bc89f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -445,8 +445,12 @@ void ipc_event_binding(struct sway_binding *binding) {
445 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); 445 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
446 json_object_object_add(json_binding, "symbols", symbols); 446 json_object_object_add(json_binding, "symbols", symbols);
447 json_object_object_add(json_binding, "symbol", symbol); 447 json_object_object_add(json_binding, "symbol", symbol);
448 json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? 448
449 json_object_new_string("mouse") : json_object_new_string("keyboard")); 449 bool mouse = binding->type == BINDING_MOUSECODE ||
450 binding->type == BINDING_MOUSESYM;
451 json_object_object_add(json_binding, "input_type", mouse
452 ? json_object_new_string("mouse")
453 : json_object_new_string("keyboard"));
450 454
451 json_object *json = json_object_new_object(); 455 json_object *json = json_object_new_object();
452 json_object_object_add(json, "change", json_object_new_string("run")); 456 json_object_object_add(json, "change", json_object_new_string("run"));
diff --git a/sway/meson.build b/sway/meson.build
index 98676ce0..ab5862c5 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -116,6 +116,8 @@ sway_sources = files(
116 'commands/bar/position.c', 116 'commands/bar/position.c',
117 'commands/bar/separator_symbol.c', 117 'commands/bar/separator_symbol.c',
118 'commands/bar/status_command.c', 118 'commands/bar/status_command.c',
119 'commands/bar/status_edge_padding.c',
120 'commands/bar/status_padding.c',
119 'commands/bar/strip_workspace_numbers.c', 121 'commands/bar/strip_workspace_numbers.c',
120 'commands/bar/strip_workspace_name.c', 122 'commands/bar/strip_workspace_name.c',
121 'commands/bar/swaybar_command.c', 123 '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 820194a9..c54babaa 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.
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 3757a097..3398ad58 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -302,7 +302,7 @@ runtime.
302``` 302```
303 303
304 *bindcode* [--release|--locked] [--input-device=<device>] [--no-warn] <code> <command> 304 *bindcode* [--release|--locked] [--input-device=<device>] [--no-warn] <code> <command>
305 is also available for binding with key codes instead of key names. 305 is also available for binding with key/button codes instead of key/button names.
306 306
307*client.<class>* <border> <background> <text> <indicator> <child\_border> 307*client.<class>* <border> <background> <text> <indicator> <child\_border>
308 Configures the color of window borders and title bars. All 5 colors are 308 Configures the color of window borders and title bars. All 5 colors are
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 95ab9378..f24be010 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -58,6 +58,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
58 wlr_output->data = output; 58 wlr_output->data = output;
59 59
60 wl_signal_add(&wlr_output->events.destroy, &output->destroy); 60 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
61 wl_signal_init(&output->events.destroy);
61 62
62 wl_list_insert(&root->all_outputs, &output->link); 63 wl_list_insert(&root->all_outputs, &output->link);
63 64
@@ -76,7 +77,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
76 for (size_t i = 0; i < len; ++i) { 77 for (size_t i = 0; i < len; ++i) {
77 wl_list_init(&output->layers[i]); 78 wl_list_init(&output->layers[i]);
78 } 79 }
79 wl_signal_init(&output->events.destroy);
80 80
81 output->enabled = true; 81 output->enabled = true;
82 list_add(root->outputs, output); 82 list_add(root->outputs, output);
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 9fe4ee9c..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
@@ -90,13 +93,24 @@ static uint32_t render_status_line_text(cairo_t *cairo,
90 return output->height; 93 return output->height;
91} 94}
92 95
93static void render_sharp_line(cairo_t *cairo, uint32_t color, 96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
94 double x, double y, double width, double height) { 97 double x, double y, double width, double height) {
98 cairo_save(cairo);
95 cairo_set_source_u32(cairo, color); 99 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height);
102 cairo_fill(cairo);
103 cairo_restore(cairo);
104}
105
106static void render_sharp_line(cairo_t *cairo, uint32_t color,
107 double x, double y, double width, double height) {
96 if (width > 1 && height > 1) { 108 if (width > 1 && height > 1) {
97 cairo_rectangle(cairo, x, y, width, height); 109 render_sharp_rectangle(cairo, color, x, y, width, height);
98 cairo_fill(cairo);
99 } else { 110 } else {
111 cairo_save(cairo);
112 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
100 if (width == 1) { 114 if (width == 1) {
101 x += 0.5; 115 x += 0.5;
102 height += y; 116 height += y;
@@ -111,6 +125,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
111 cairo_set_line_width(cairo, 1.0); 125 cairo_set_line_width(cairo, 1.0);
112 cairo_line_to(cairo, width, height); 126 cairo_line_to(cairo, width, height);
113 cairo_stroke(cairo); 127 cairo_stroke(cairo);
128 cairo_restore(cairo);
114 } 129 }
115} 130}
116 131
@@ -141,7 +156,7 @@ static uint32_t render_status_block(cairo_t *cairo,
141 output->scale, block->markup, "%s", block->full_text); 156 output->scale, block->markup, "%s", block->full_text);
142 157
143 int margin = 3 * output->scale; 158 int margin = 3 * output->scale;
144 int ws_vertical_padding = WS_VERTICAL_PADDING * 2; 159 double ws_vertical_padding = config->status_padding * output->scale;
145 160
146 int width = text_width; 161 int width = text_width;
147 if (width < block->min_width) { 162 if (width < block->min_width) {
@@ -151,18 +166,19 @@ static uint32_t render_status_block(cairo_t *cairo,
151 double block_width = width; 166 double block_width = width;
152 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 167 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
153 uint32_t ideal_surface_height = ideal_height / output->scale; 168 uint32_t ideal_surface_height = ideal_height / output->scale;
154 if (output->height < ideal_surface_height) { 169 if (!output->bar->config->height &&
170 output->height < ideal_surface_height) {
155 return ideal_surface_height; 171 return ideal_surface_height;
156 } 172 }
157 173
158 *x -= width; 174 *x -= width;
159 if ((block->border || block->urgent) && block->border_left > 0) { 175 if ((block->border || block->urgent) && block->border_left > 0) {
160 *x -= (block->border_left + margin); 176 *x -= (block->border_left * output->scale + margin);
161 block_width += block->border_left + margin; 177 block_width += block->border_left * output->scale + margin;
162 } 178 }
163 if ((block->border || block->urgent) && block->border_right > 0) { 179 if ((block->border || block->urgent) && block->border_right > 0) {
164 *x -= (block->border_right + margin); 180 *x -= (block->border_right * output->scale + margin);
165 block_width += block->border_right + margin; 181 block_width += block->border_right * output->scale + margin;
166 } 182 }
167 183
168 int sep_width, sep_height; 184 int sep_width, sep_height;
@@ -173,7 +189,8 @@ static uint32_t render_status_block(cairo_t *cairo,
173 output->scale, false, "%s", config->sep_symbol); 189 output->scale, false, "%s", config->sep_symbol);
174 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 190 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
175 uint32_t _ideal_surface_height = _ideal_height / output->scale; 191 uint32_t _ideal_surface_height = _ideal_height / output->scale;
176 if (output->height < _ideal_surface_height) { 192 if (!output->bar->config->height &&
193 output->height < _ideal_surface_height) {
177 return _ideal_surface_height; 194 return _ideal_surface_height;
178 } 195 }
179 if (sep_width > sep_block_width) { 196 if (sep_width > sep_block_width) {
@@ -181,8 +198,8 @@ static uint32_t render_status_block(cairo_t *cairo,
181 } 198 }
182 } 199 }
183 *x -= sep_block_width; 200 *x -= sep_block_width;
184 } else { 201 } else if (config->status_edge_padding) {
185 *x -= margin; 202 *x -= config->status_edge_padding * output->scale;
186 } 203 }
187 204
188 uint32_t height = output->height * output->scale; 205 uint32_t height = output->height * output->scale;
@@ -199,48 +216,41 @@ static uint32_t render_status_block(cairo_t *cairo,
199 wl_list_insert(&output->hotspots, &hotspot->link); 216 wl_list_insert(&output->hotspots, &hotspot->link);
200 } 217 }
201 218
202 double pos = *x; 219 double x_pos = *x;
220 double y_pos = ws_vertical_padding;
221 double render_height = height - ws_vertical_padding * 2;
203 222
204 uint32_t bg_color = block->urgent 223 uint32_t bg_color = block->urgent
205 ? config->colors.urgent_workspace.background : block->background; 224 ? config->colors.urgent_workspace.background : block->background;
206 if (bg_color) { 225 if (bg_color) {
207 cairo_set_source_u32(cairo, bg_color); 226 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
208 cairo_rectangle(cairo, pos - 0.5 * output->scale, 227 block_width, render_height);
209 output->scale, width, height);
210 cairo_fill(cairo);
211 } 228 }
212 229
213 uint32_t border_color = block->urgent 230 uint32_t border_color = block->urgent
214 ? config->colors.urgent_workspace.border : block->border; 231 ? config->colors.urgent_workspace.border : block->border;
215 if (border_color && block->border_top > 0) { 232 if (border_color && block->border_top > 0) {
216 render_sharp_line(cairo, border_color, 233 render_sharp_line(cairo, border_color, x_pos, y_pos,
217 pos - 0.5 * output->scale, output->scale, 234 block_width, block->border_top * output->scale);
218 text_width, block->border_top);
219 } 235 }
220 if (border_color && block->border_bottom > 0) { 236 if (border_color && block->border_bottom > 0) {
221 render_sharp_line(cairo, border_color, 237 render_sharp_line(cairo, border_color, x_pos,
222 pos - 0.5 * output->scale, 238 y_pos + render_height - block->border_bottom * output->scale,
223 height - output->scale - block->border_bottom, 239 block_width, block->border_bottom * output->scale);
224 text_width, block->border_bottom);
225 }
226 if (border_color != 0 && block->border_left > 0) {
227 render_sharp_line(cairo, border_color,
228 pos - 0.5 * output->scale, output->scale,
229 block->border_left, height);
230 } 240 }
231 if (border_color != 0 && block->border_right > 0) { 241 if (border_color && block->border_left > 0) {
232 render_sharp_line(cairo, border_color, 242 render_sharp_line(cairo, border_color, x_pos, y_pos,
233 pos - 0.5 * output->scale + text_width, output->scale, 243 block->border_left * output->scale, render_height);
234 block->border_right, height); 244 x_pos += block->border_left * output->scale + margin;
235 } 245 }
236 246
237 double offset = 0; 247 double offset = 0;
238 if (strncmp(block->align, "left", 5) == 0) { 248 if (strncmp(block->align, "left", 5) == 0) {
239 offset = pos; 249 offset = x_pos;
240 } else if (strncmp(block->align, "right", 5) == 0) { 250 } else if (strncmp(block->align, "right", 5) == 0) {
241 offset = pos + width - text_width; 251 offset = x_pos + width - text_width;
242 } else if (strncmp(block->align, "center", 6) == 0) { 252 } else if (strncmp(block->align, "center", 6) == 0) {
243 offset = pos + (width - text_width) / 2; 253 offset = x_pos + (width - text_width) / 2;
244 } 254 }
245 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); 255 cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0);
246 uint32_t color = block->color ? *block->color : config->colors.statusline; 256 uint32_t color = block->color ? *block->color : config->colors.statusline;
@@ -248,14 +258,13 @@ static uint32_t render_status_block(cairo_t *cairo,
248 cairo_set_source_u32(cairo, color); 258 cairo_set_source_u32(cairo, color);
249 pango_printf(cairo, config->font, output->scale, 259 pango_printf(cairo, config->font, output->scale,
250 block->markup, "%s", block->full_text); 260 block->markup, "%s", block->full_text);
251 pos += width; 261 x_pos += width;
252 262
253 if (block->border && block->border_right > 0) { 263 if (block->border && block->border_right > 0) {
254 pos += margin; 264 x_pos += margin;
255 render_sharp_line(cairo, block->border, 265 render_sharp_line(cairo, border_color, x_pos, y_pos,
256 pos - 0.5 * output->scale, output->scale, 266 block->border_right * output->scale, render_height);
257 block->border_right, height); 267 x_pos += block->border_right * output->scale;
258 pos += block->border_right;
259 } 268 }
260 269
261 if (!edge && block->separator) { 270 if (!edge && block->separator) {
@@ -265,14 +274,14 @@ static uint32_t render_status_block(cairo_t *cairo,
265 cairo_set_source_u32(cairo, config->colors.separator); 274 cairo_set_source_u32(cairo, config->colors.separator);
266 } 275 }
267 if (config->sep_symbol) { 276 if (config->sep_symbol) {
268 offset = pos + (sep_block_width - sep_width) / 2; 277 offset = x_pos + (sep_block_width - sep_width) / 2;
269 cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0); 278 cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0);
270 pango_printf(cairo, config->font, output->scale, false, 279 pango_printf(cairo, config->font, output->scale, false,
271 "%s", config->sep_symbol); 280 "%s", config->sep_symbol);
272 } else { 281 } else {
273 cairo_set_line_width(cairo, 1); 282 cairo_set_line_width(cairo, 1);
274 cairo_move_to(cairo, pos + sep_block_width / 2, margin); 283 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
275 cairo_line_to(cairo, pos + sep_block_width / 2, height - margin); 284 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
276 cairo_stroke(cairo); 285 cairo_stroke(cairo);
277 } 286 }
278 } 287 }
@@ -282,7 +291,7 @@ static uint32_t render_status_block(cairo_t *cairo,
282static uint32_t render_status_line_i3bar(cairo_t *cairo, 291static uint32_t render_status_line_i3bar(cairo_t *cairo,
283 struct swaybar_output *output, double *x) { 292 struct swaybar_output *output, double *x) {
284 uint32_t max_height = 0; 293 uint32_t max_height = 0;
285 bool edge = true; 294 bool edge = *x == output->width * output->scale;
286 struct i3bar_block *block; 295 struct i3bar_block *block;
287 wl_list_for_each(block, &output->bar->status->blocks, link) { 296 wl_list_for_each(block, &output->bar->status->blocks, link) {
288 uint32_t h = render_status_block(cairo, output, block, x, edge); 297 uint32_t h = render_status_block(cairo, output, block, x, edge);
@@ -328,7 +337,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
328 uint32_t ideal_height = text_height + ws_vertical_padding * 2 337 uint32_t ideal_height = text_height + ws_vertical_padding * 2
329 + border_width * 2; 338 + border_width * 2;
330 uint32_t ideal_surface_height = ideal_height / output->scale; 339 uint32_t ideal_surface_height = ideal_height / output->scale;
331 if (output->height < ideal_surface_height) { 340 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) {
332 return ideal_surface_height; 342 return ideal_surface_height;
333 } 343 }
334 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;
@@ -394,7 +404,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
394 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 404 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
395 + border_width * 2; 405 + border_width * 2;
396 uint32_t ideal_surface_height = ideal_height / output->scale; 406 uint32_t ideal_surface_height = ideal_height / output->scale;
397 if (output->height < ideal_surface_height) { 407 if (!output->bar->config->height &&
408 output->height < ideal_surface_height) {
398 return ideal_surface_height; 409 return ideal_surface_height;
399 } 410 }
400 411
@@ -521,7 +532,7 @@ void render_frame(struct swaybar_output *output) {
521 cairo_restore(cairo); 532 cairo_restore(cairo);
522 uint32_t height = render_to_cairo(cairo, output); 533 uint32_t height = render_to_cairo(cairo, output);
523 int config_height = output->bar->config->height; 534 int config_height = output->bar->config->height;
524 if (config_height >= 0 && height < (uint32_t)config_height) { 535 if (config_height > 0) {
525 height = config_height; 536 height = config_height;
526 } 537 }
527 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