summaryrefslogtreecommitdiffstats
path: root/swayidle
diff options
context:
space:
mode:
Diffstat (limited to 'swayidle')
-rw-r--r--swayidle/main.c442
-rw-r--r--swayidle/meson.build27
-rw-r--r--swayidle/swayidle.1.scd63
3 files changed, 0 insertions, 532 deletions
diff --git a/swayidle/main.c b/swayidle/main.c
deleted file mode 100644
index 2b185949..00000000
--- a/swayidle/main.c
+++ /dev/null
@@ -1,442 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <errno.h>
4#include <getopt.h>
5#include <pthread.h>
6#include <signal.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/wait.h>
11#include <unistd.h>
12#include <wayland-client-protocol.h>
13#include <wayland-client.h>
14#include <wayland-server.h>
15#include <wayland-util.h>
16#include <wlr/config.h>
17#include <wlr/util/log.h>
18#include "config.h"
19#include "idle-client-protocol.h"
20#include "list.h"
21#if HAVE_SYSTEMD
22#include <systemd/sd-bus.h>
23#include <systemd/sd-login.h>
24#elif HAVE_ELOGIND
25#include <elogind/sd-bus.h>
26#include <elogind/sd-login.h>
27#endif
28
29static struct org_kde_kwin_idle *idle_manager = NULL;
30static struct wl_seat *seat = NULL;
31
32struct swayidle_state {
33 struct wl_display *display;
34 struct wl_event_loop *event_loop;
35 list_t *timeout_cmds; // struct swayidle_timeout_cmd *
36 char *lock_cmd;
37} state;
38
39struct swayidle_timeout_cmd {
40 int timeout, registered_timeout;
41 struct org_kde_kwin_idle_timeout *idle_timer;
42 char *idle_cmd;
43 char *resume_cmd;
44};
45
46static void cmd_exec(char *param) {
47 wlr_log(WLR_DEBUG, "Cmd exec %s", param);
48 pid_t pid = fork();
49 if (pid == 0) {
50 pid = fork();
51 if (pid == 0) {
52 char *const cmd[] = { "sh", "-c", param, NULL, };
53 execvp(cmd[0], cmd);
54 wlr_log_errno(WLR_ERROR, "execve failed!");
55 exit(1);
56 } else if (pid < 0) {
57 wlr_log_errno(WLR_ERROR, "fork failed");
58 exit(1);
59 }
60 exit(0);
61 } else if (pid < 0) {
62 wlr_log_errno(WLR_ERROR, "fork failed");
63 } else {
64 wlr_log(WLR_DEBUG, "Spawned process %s", param);
65 waitpid(pid, NULL, 0);
66 }
67}
68
69#if HAVE_SYSTEMD || HAVE_ELOGIND
70static int lock_fd = -1;
71static int ongoing_fd = -1;
72static struct sd_bus *bus = NULL;
73
74static int release_lock(void *data) {
75 wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
76 if (ongoing_fd >= 0) {
77 close(ongoing_fd);
78 }
79 ongoing_fd = -1;
80 return 0;
81}
82
83static void acquire_sleep_lock(void) {
84 sd_bus_message *msg;
85 sd_bus_error error;
86 int ret = sd_bus_call_method(bus, "org.freedesktop.login1",
87 "/org/freedesktop/login1",
88 "org.freedesktop.login1.Manager", "Inhibit",
89 &error, &msg, "ssss", "sleep", "swayidle",
90 "Setup Up Lock Screen", "delay");
91 if (ret < 0) {
92 wlr_log(WLR_ERROR, "Failed to send Inhibit signal: %s",
93 strerror(-ret));
94 return;
95 }
96
97 ret = sd_bus_message_read(msg, "h", &lock_fd);
98 if (ret < 0) {
99 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
100 strerror(-ret));
101 return;
102 }
103
104 wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
105}
106
107static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
108 sd_bus_error *ret_error) {
109 /* "b" apparently reads into an int, not a bool */
110 int going_down = 1;
111 int ret = sd_bus_message_read(msg, "b", &going_down);
112 if (ret < 0) {
113 wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
114 strerror(-ret));
115 }
116 wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);
117 if (!going_down) {
118 acquire_sleep_lock();
119 return 0;
120 }
121
122 ongoing_fd = lock_fd;
123
124 if (state.lock_cmd) {
125 cmd_exec(state.lock_cmd);
126 }
127
128 if (ongoing_fd >= 0) {
129 struct wl_event_source *source =
130 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
131 wl_event_source_timer_update(source, 1000);
132 }
133
134 wlr_log(WLR_DEBUG, "Prepare for sleep done");
135 return 0;
136}
137
138static int dbus_event(int fd, uint32_t mask, void *data) {
139 sd_bus *bus = data;
140 while (sd_bus_process(bus, NULL) > 0) {
141 // Do nothing.
142 }
143 return 1;
144}
145
146static void setup_sleep_listener(void) {
147 int ret = sd_bus_default_system(&bus);
148 if (ret < 0) {
149 wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s",
150 strerror(-ret));
151 return;
152 }
153
154 char str[256];
155 const char *fmt = "type='signal',"
156 "sender='org.freedesktop.login1',"
157 "interface='org.freedesktop.login1.%s',"
158 "member='%s'," "path='%s'";
159
160 snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep",
161 "/org/freedesktop/login1");
162 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
163 if (ret < 0) {
164 wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
165 return;
166 }
167 acquire_sleep_lock();
168
169 wl_event_loop_add_fd(state.event_loop, sd_bus_get_fd(bus),
170 WL_EVENT_READABLE, dbus_event, bus);
171}
172#endif
173
174static void handle_global(void *data, struct wl_registry *registry,
175 uint32_t name, const char *interface, uint32_t version) {
176 if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) {
177 idle_manager =
178 wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
179 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
180 seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
181 }
182}
183
184static void handle_global_remove(void *data, struct wl_registry *registry,
185 uint32_t name) {
186 // Who cares
187}
188
189static const struct wl_registry_listener registry_listener = {
190 .global = handle_global,
191 .global_remove = handle_global_remove,
192};
193
194static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener;
195
196static void register_timeout(struct swayidle_timeout_cmd *cmd,
197 int timeout) {
198 if (cmd->idle_timer != NULL) {
199 org_kde_kwin_idle_timeout_destroy(cmd->idle_timer);
200 cmd->idle_timer = NULL;
201 }
202 if (timeout < 0) {
203 wlr_log(WLR_DEBUG, "Not registering idle timeout");
204 return;
205 }
206 wlr_log(WLR_DEBUG, "Register with timeout: %d", timeout);
207 cmd->idle_timer =
208 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout);
209 org_kde_kwin_idle_timeout_add_listener(cmd->idle_timer,
210 &idle_timer_listener, cmd);
211 cmd->registered_timeout = timeout;
212}
213
214static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
215 struct swayidle_timeout_cmd *cmd = data;
216 wlr_log(WLR_DEBUG, "idle state");
217 if (cmd->idle_cmd) {
218 cmd_exec(cmd->idle_cmd);
219 }
220}
221
222static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
223 struct swayidle_timeout_cmd *cmd = data;
224 wlr_log(WLR_DEBUG, "active state");
225 if (cmd->registered_timeout != cmd->timeout) {
226 register_timeout(cmd, cmd->timeout);
227 }
228 if (cmd->resume_cmd) {
229 cmd_exec(cmd->resume_cmd);
230 }
231}
232
233static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
234 .idle = handle_idle,
235 .resumed = handle_resume,
236};
237
238static char *parse_command(int argc, char **argv) {
239 if (argc < 1) {
240 wlr_log(WLR_ERROR, "Missing command");
241 return NULL;
242 }
243
244 wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
245 return strdup(argv[0]);
246}
247
248static int parse_timeout(int argc, char **argv) {
249 if (argc < 3) {
250 wlr_log(WLR_ERROR, "Too few parameters to timeout command. "
251 "Usage: timeout <seconds> <command>");
252 exit(-1);
253 }
254 errno = 0;
255 char *endptr;
256 int seconds = strtoul(argv[1], &endptr, 10);
257 if (errno != 0 || *endptr != '\0') {
258 wlr_log(WLR_ERROR, "Invalid timeout parameter '%s', it should be a "
259 "numeric value representing seconds", optarg);
260 exit(-1);
261 }
262
263 struct swayidle_timeout_cmd *cmd =
264 calloc(1, sizeof(struct swayidle_timeout_cmd));
265
266 if (seconds > 0) {
267 cmd->timeout = seconds * 1000;
268 } else {
269 cmd->timeout = -1;
270 }
271
272 wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
273 wlr_log(WLR_DEBUG, "Setup idle");
274 cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
275
276 int result = 3;
277 if (argc >= 5 && !strcmp("resume", argv[3])) {
278 wlr_log(WLR_DEBUG, "Setup resume");
279 cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
280 result = 5;
281 }
282 list_add(state.timeout_cmds, cmd);
283 return result;
284}
285
286static int parse_sleep(int argc, char **argv) {
287 if (argc < 2) {
288 wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
289 "Usage: before-sleep <command>");
290 exit(-1);
291 }
292
293 state.lock_cmd = parse_command(argc - 1, &argv[1]);
294 if (state.lock_cmd) {
295 wlr_log(WLR_DEBUG, "Setup sleep lock: %s", state.lock_cmd);
296 }
297
298 return 2;
299}
300
301static int parse_args(int argc, char *argv[]) {
302 bool debug = false;
303
304 int c;
305 while ((c = getopt(argc, argv, "hd")) != -1) {
306 switch (c) {
307 case 'd':
308 debug = true;
309 break;
310 case 'h':
311 case '?':
312 printf("Usage: %s [OPTIONS]\n", argv[0]);
313 printf(" -d\tdebug\n");
314 printf(" -h\tthis help menu\n");
315 return 1;
316 default:
317 return 1;
318 }
319 }
320
321 wlr_log_init(debug ? WLR_DEBUG : WLR_INFO, NULL);
322
323 state.timeout_cmds = create_list();
324
325 int i = optind;
326 while (i < argc) {
327 if (!strcmp("timeout", argv[i])) {
328 wlr_log(WLR_DEBUG, "Got timeout");
329 i += parse_timeout(argc - i, &argv[i]);
330 } else if (!strcmp("before-sleep", argv[i])) {
331 wlr_log(WLR_DEBUG, "Got before-sleep");
332 i += parse_sleep(argc - i, &argv[i]);
333 } else {
334 wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
335 return 1;
336 }
337 }
338
339 return 0;
340}
341
342void sway_terminate(int exit_code) {
343 wl_display_disconnect(state.display);
344 wl_event_loop_destroy(state.event_loop);
345 exit(exit_code);
346}
347
348static void register_zero_idle_timeout(void *item) {
349 struct swayidle_timeout_cmd *cmd = item;
350 register_timeout(cmd, 0);
351}
352
353static int handle_signal(int sig, void *data) {
354 switch (sig) {
355 case SIGINT:
356 case SIGTERM:
357 sway_terminate(0);
358 return 0;
359 case SIGUSR1:
360 wlr_log(WLR_DEBUG, "Got SIGUSR1");
361 list_foreach(state.timeout_cmds, register_zero_idle_timeout);
362 return 1;
363 }
364 assert(false); // not reached
365}
366
367static int display_event(int fd, uint32_t mask, void *data) {
368 if (mask & WL_EVENT_HANGUP) {
369 sway_terminate(0);
370 }
371 if (wl_display_dispatch(state.display) < 0) {
372 wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
373 sway_terminate(0);
374 }
375 wl_display_flush(state.display);
376 return 0;
377}
378
379static void register_idle_timeout(void *item) {
380 struct swayidle_timeout_cmd *cmd = item;
381 register_timeout(cmd, cmd->timeout);
382}
383
384int main(int argc, char *argv[]) {
385 if (parse_args(argc, argv) != 0) {
386 return -1;
387 }
388
389 state.event_loop = wl_event_loop_create();
390
391 wl_event_loop_add_signal(state.event_loop, SIGINT, handle_signal, NULL);
392 wl_event_loop_add_signal(state.event_loop, SIGTERM, handle_signal, NULL);
393 wl_event_loop_add_signal(state.event_loop, SIGUSR1, handle_signal, NULL);
394
395 state.display = wl_display_connect(NULL);
396 if (state.display == NULL) {
397 wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
398 "If your compositor is running, check or set the "
399 "WAYLAND_DISPLAY environment variable.");
400 return -3;
401 }
402
403 struct wl_registry *registry = wl_display_get_registry(state.display);
404 wl_registry_add_listener(registry, &registry_listener, NULL);
405 wl_display_roundtrip(state.display);
406
407 if (idle_manager == NULL) {
408 wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
409 return -4;
410 }
411 if (seat == NULL) {
412 wlr_log(WLR_ERROR, "Seat error");
413 return -5;
414 }
415
416 bool should_run = state.timeout_cmds->length > 0;
417#if HAVE_SYSTEMD || HAVE_ELOGIND
418 if (state.lock_cmd) {
419 should_run = true;
420 setup_sleep_listener();
421 }
422#endif
423 if (!should_run) {
424 wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
425 sway_terminate(0);
426 }
427
428 list_foreach(state.timeout_cmds, register_idle_timeout);
429
430 wl_display_roundtrip(state.display);
431
432 struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
433 wl_display_get_fd(state.display), WL_EVENT_READABLE,
434 display_event, NULL);
435 wl_event_source_check(source);
436
437 while (wl_event_loop_dispatch(state.event_loop, -1) != 1) {
438 // This space intentionally left blank
439 }
440
441 sway_terminate(0);
442}
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 3083163f..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 600 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)