aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Mattias Eriksson <snaggen@mayam.com>2018-04-17 09:54:02 +0200
committerLibravatar Mattias Eriksson <snaggen@mayam.com>2018-05-13 00:30:09 +0200
commit8fbafbfab5671d56dd469f2205b7906c4a7f7c7c (patch)
treeab4eab0020d97dc5091b72479c383989ccc84729
parentMerge pull request #1967 from emersion/remove-xdg-popup-unmap (diff)
downloadsway-8fbafbfab5671d56dd469f2205b7906c4a7f7c7c.tar.gz
sway-8fbafbfab5671d56dd469f2205b7906c4a7f7c7c.tar.zst
sway-8fbafbfab5671d56dd469f2205b7906c4a7f7c7c.zip
Idle handling for dpms/lockscreen et al
Swayidle handles idle events and allows for dpms and lockscreen handling. It also handles systemd sleep events, and can raise a lockscreen on sleep Fixes #541
-rw-r--r--config.in14
-rw-r--r--include/sway/config.h7
-rw-r--r--include/sway/server.h1
-rw-r--r--meson.build16
-rw-r--r--protocols/idle.xml49
-rw-r--r--protocols/meson.build1
-rw-r--r--sway/commands/output.c25
-rw-r--r--sway/config.c1
-rw-r--r--sway/config/output.c17
-rw-r--r--sway/input/cursor.c11
-rw-r--r--sway/input/keyboard.c2
-rw-r--r--sway/server.c2
-rw-r--r--swayidle/main.c414
-rw-r--r--swayidle/meson.build17
-rw-r--r--swayidle/swayidle.1.scd61
-rw-r--r--swayidle/swayidle.1.txt67
16 files changed, 703 insertions, 2 deletions
diff --git a/config.in b/config.in
index 54817f2a..4a11762a 100644
--- a/config.in
+++ b/config.in
@@ -29,6 +29,20 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
29# 29#
30# You can get the names of your outputs by running: swaymsg -t get_outputs 30# You can get the names of your outputs by running: swaymsg -t get_outputs
31 31
32### Idle configuration
33#
34# Example configuration:
35#
36#exec swayidle \
37# timeout 300 'swaylock -c 000000' \
38# timeout 600 'swaymsg "output * dpms off"' \
39# resume 'swaymsg "output * dpms on"' \
40# before-sleep 'swaylock -c 000000'
41#
42# This will lock your screen after 300 seconds of inactivity, then turn off
43# your displays after another 600 seconds, and turn your screens back on when
44# resumed. It will also lock your screen before your computer goes to sleep.
45
32### Input configuration 46### Input configuration
33# 47#
34# Example configuration: 48# Example configuration:
diff --git a/include/sway/config.h b/include/sway/config.h
index b20458cb..f77c3b50 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -107,6 +107,12 @@ struct seat_config {
107 list_t *attachments; // list of seat_attachment configs 107 list_t *attachments; // list of seat_attachment configs
108}; 108};
109 109
110enum config_dpms {
111 DPMS_IGNORE,
112 DPMS_ON,
113 DPMS_OFF
114};
115
110/** 116/**
111 * Size and position configuration for a particular output. 117 * Size and position configuration for a particular output.
112 * 118 *
@@ -123,6 +129,7 @@ struct output_config {
123 129
124 char *background; 130 char *background;
125 char *background_option; 131 char *background_option;
132 enum config_dpms dpms_state;
126}; 133};
127 134
128/** 135/**
diff --git a/include/sway/server.h b/include/sway/server.h
index 296fbf22..ac685bf8 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -21,6 +21,7 @@ struct sway_server {
21 21
22 struct wlr_compositor *compositor; 22 struct wlr_compositor *compositor;
23 struct wlr_data_device_manager *data_device_manager; 23 struct wlr_data_device_manager *data_device_manager;
24 struct wlr_idle *idle;
24 25
25 struct sway_input_manager *input; 26 struct sway_input_manager *input;
26 27
diff --git a/meson.build b/meson.build
index a1f406ec..b943236f 100644
--- a/meson.build
+++ b/meson.build
@@ -20,6 +20,8 @@ datadir = get_option('datadir')
20sysconfdir = get_option('sysconfdir') 20sysconfdir = get_option('sysconfdir')
21prefix = get_option('prefix') 21prefix = get_option('prefix')
22 22
23swayidle_deps = []
24
23jsonc = dependency('json-c', version: '>=0.13') 25jsonc = dependency('json-c', version: '>=0.13')
24pcre = dependency('libpcre') 26pcre = dependency('libpcre')
25wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) 27wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
@@ -37,6 +39,8 @@ pixman = dependency('pixman-1')
37libcap = dependency('libcap') 39libcap = dependency('libcap')
38libinput = dependency('libinput', version: '>=1.6.0') 40libinput = dependency('libinput', version: '>=1.6.0')
39libpam = cc.find_library('pam') 41libpam = cc.find_library('pam')
42systemd = dependency('libsystemd', required: false)
43elogind = dependency('libelogind', required: false)
40math = cc.find_library('m') 44math = cc.find_library('m')
41rt = cc.find_library('rt') 45rt = cc.find_library('rt')
42git = find_program('git', required: false) 46git = find_program('git', required: false)
@@ -47,6 +51,16 @@ if gdk_pixbuf.found()
47 conf_data.set('HAVE_GDK_PIXBUF', true) 51 conf_data.set('HAVE_GDK_PIXBUF', true)
48endif 52endif
49 53
54if systemd.found()
55 conf_data.set('SWAY_IDLE_HAS_SYSTEMD', true)
56 swayidle_deps += systemd
57endif
58
59if elogind.found()
60 conf_data.set('SWAY_IDLE_HAS_ELOGIND', true)
61 swayidle_deps += elogind
62endif
63
50scdoc = find_program('scdoc', required: false) 64scdoc = find_program('scdoc', required: false)
51 65
52if scdoc.found() 66if scdoc.found()
@@ -59,6 +73,7 @@ if scdoc.found()
59 'sway/sway-input.5.scd', 73 'sway/sway-input.5.scd',
60 'swaylock/swaylock.1.scd', 74 'swaylock/swaylock.1.scd',
61 'swaymsg/swaymsg.1.scd', 75 'swaymsg/swaymsg.1.scd',
76 'swayidle/swayidle.1.scd',
62 ] 77 ]
63 foreach filename : man_files 78 foreach filename : man_files
64 topic = filename.split('.')[-3].split('/')[-1] 79 topic = filename.split('.')[-3].split('/')[-1]
@@ -106,6 +121,7 @@ subdir('client')
106subdir('swaybg') 121subdir('swaybg')
107subdir('swaybar') 122subdir('swaybar')
108subdir('swaylock') 123subdir('swaylock')
124subdir('swayidle')
109 125
110config = configuration_data() 126config = configuration_data()
111config.set('sysconfdir', join_paths(prefix, sysconfdir)) 127config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/protocols/idle.xml b/protocols/idle.xml
new file mode 100644
index 00000000..92d9989c
--- /dev/null
+++ b/protocols/idle.xml
@@ -0,0 +1,49 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<protocol name="idle">
3 <copyright><![CDATA[
4 Copyright (C) 2015 Martin Gräßlin
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 ]]></copyright>
19 <interface name="org_kde_kwin_idle" version="1">
20 <description summary="User idle time manager">
21 This interface allows to monitor user idle time on a given seat. The interface
22 allows to register timers which trigger after no user activity was registered
23 on the seat for a given interval. It notifies when user activity resumes.
24
25 This is useful for applications wanting to perform actions when the user is not
26 interacting with the system, e.g. chat applications setting the user as away, power
27 management features to dim screen, etc..
28 </description>
29 <request name="get_idle_timeout">
30 <arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
31 <arg name="seat" type="object" interface="wl_seat"/>
32 <arg name="timeout" type="uint" summary="The idle timeout in msec"/>
33 </request>
34 </interface>
35 <interface name="org_kde_kwin_idle_timeout" version="1">
36 <request name="release" type="destructor">
37 <description summary="release the timeout object"/>
38 </request>
39 <request name="simulate_user_activity">
40 <description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
41 </request>
42 <event name="idle">
43 <description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
44 </event>
45 <event name="resumed">
46 <description summary="Triggered on the first user activity after an idle event"/>
47 </event>
48 </interface>
49</protocol>
diff --git a/protocols/meson.build b/protocols/meson.build
index 9d213f81..fa6d696c 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -30,6 +30,7 @@ wayland_scanner_server = generator(
30client_protocols = [ 30client_protocols = [
31 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 31 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
32 ['wlr-layer-shell-unstable-v1.xml'], 32 ['wlr-layer-shell-unstable-v1.xml'],
33 ['idle.xml'],
33 ['wlr-input-inhibitor-unstable-v1.xml'] 34 ['wlr-input-inhibitor-unstable-v1.xml']
34] 35]
35 36
diff --git a/sway/commands/output.c b/sway/commands/output.c
index f7e3372c..e8881f77 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -20,6 +20,25 @@ static char *bg_options[] = {
20 "tile", 20 "tile",
21}; 21};
22 22
23static struct cmd_results *cmd_output_dpms(struct output_config *output,
24 int *i, int argc, char **argv) {
25
26 if (++*i >= argc) {
27 return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
28 }
29
30 char *value = argv[*i];
31 if (strcmp(value, "on") == 0) {
32 output->dpms_state = DPMS_ON;
33 } else if (strcmp(value, "off") == 0) {
34 output->dpms_state = DPMS_OFF;
35 } else {
36 return cmd_results_new(CMD_INVALID, "output",
37 "Invalid dpms state, valid states are on/off.");
38 }
39 return NULL;
40}
41
23static struct cmd_results *cmd_output_mode(struct output_config *output, 42static struct cmd_results *cmd_output_mode(struct output_config *output,
24 int *i, int argc, char **argv) { 43 int *i, int argc, char **argv) {
25 if (++*i >= argc) { 44 if (++*i >= argc) {
@@ -263,6 +282,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
263 } else if (strcasecmp(command, "background") == 0 || 282 } else if (strcasecmp(command, "background") == 0 ||
264 strcasecmp(command, "bg") == 0) { 283 strcasecmp(command, "bg") == 0) {
265 error = cmd_output_background(output, &i, argc, argv); 284 error = cmd_output_background(output, &i, argc, argv);
285 } else if (strcasecmp(command, "dpms") == 0) {
286 error = cmd_output_dpms(output, &i, argc, argv);
266 } else { 287 } else {
267 error = cmd_results_new(CMD_INVALID, "output", 288 error = cmd_results_new(CMD_INVALID, "output",
268 "Invalid output subcommand: %s.", command); 289 "Invalid output subcommand: %s.", command);
@@ -285,10 +306,10 @@ struct cmd_results *cmd_output(int argc, char **argv) {
285 } 306 }
286 307
287 wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 308 wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
288 "position %d,%d scale %f transform %d) (bg %s %s)", 309 "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)",
289 output->name, output->enabled, output->width, output->height, 310 output->name, output->enabled, output->width, output->height,
290 output->refresh_rate, output->x, output->y, output->scale, 311 output->refresh_rate, output->x, output->y, output->scale,
291 output->transform, output->background, output->background_option); 312 output->transform, output->background, output->background_option, output->dpms_state);
292 313
293 // Try to find the output container and apply configuration now. If 314 // Try to find the output container and apply configuration now. If
294 // this is during startup then there will be no container and config 315 // this is during startup then there will be no container and config
diff --git a/sway/config.c b/sway/config.c
index 270c1d86..34c8a280 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -165,6 +165,7 @@ static void config_defaults(struct sway_config *config) {
165 config->floating_mod = 0; 165 config->floating_mod = 0;
166 config->dragging_key = BTN_LEFT; 166 config->dragging_key = BTN_LEFT;
167 config->resizing_key = BTN_RIGHT; 167 config->resizing_key = BTN_RIGHT;
168
168 if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; 169 if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
169 if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; 170 if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
170 if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; 171 if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;
diff --git a/sway/config/output.c b/sway/config/output.c
index 68022278..ee2440ea 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -81,6 +81,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
81 free(dst->background_option); 81 free(dst->background_option);
82 dst->background_option = strdup(src->background_option); 82 dst->background_option = strdup(src->background_option);
83 } 83 }
84 if (src->dpms_state != 0) {
85 dst->dpms_state = src->dpms_state;
86 }
84} 87}
85 88
86static void set_mode(struct wlr_output *output, int width, int height, 89static void set_mode(struct wlr_output *output, int width, int height,
@@ -204,6 +207,20 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
204 execvp(cmd[0], cmd); 207 execvp(cmd[0], cmd);
205 } 208 }
206 } 209 }
210 if (oc && oc->dpms_state != DPMS_IGNORE) {
211 switch (oc->dpms_state) {
212 case DPMS_ON:
213 wlr_log(L_DEBUG, "Turning on screen");
214 wlr_output_enable(wlr_output, true);
215 break;
216 case DPMS_OFF:
217 wlr_log(L_DEBUG, "Turning off screen");
218 wlr_output_enable(wlr_output, false);
219 break;
220 case DPMS_IGNORE:
221 break;
222 }
223 }
207} 224}
208 225
209void free_output_config(struct output_config *oc) { 226void free_output_config(struct output_config *oc) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 51ce86e1..9259c475 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -7,6 +7,7 @@
7#endif 7#endif
8#include <wlr/types/wlr_cursor.h> 8#include <wlr/types/wlr_cursor.h>
9#include <wlr/types/wlr_xcursor_manager.h> 9#include <wlr/types/wlr_xcursor_manager.h>
10#include <wlr/types/wlr_idle.h>
10#include "list.h" 11#include "list.h"
11#include "log.h" 12#include "log.h"
12#include "sway/input/cursor.h" 13#include "sway/input/cursor.h"
@@ -172,6 +173,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec)
172 173
173static void handle_cursor_motion(struct wl_listener *listener, void *data) { 174static void handle_cursor_motion(struct wl_listener *listener, void *data) {
174 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); 175 struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
176 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
175 struct wlr_event_pointer_motion *event = data; 177 struct wlr_event_pointer_motion *event = data;
176 wlr_cursor_move(cursor->cursor, event->device, 178 wlr_cursor_move(cursor->cursor, event->device,
177 event->delta_x, event->delta_y); 179 event->delta_x, event->delta_y);
@@ -182,6 +184,7 @@ static void handle_cursor_motion_absolute(
182 struct wl_listener *listener, void *data) { 184 struct wl_listener *listener, void *data) {
183 struct sway_cursor *cursor = 185 struct sway_cursor *cursor =
184 wl_container_of(listener, cursor, motion_absolute); 186 wl_container_of(listener, cursor, motion_absolute);
187 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
185 struct wlr_event_pointer_motion_absolute *event = data; 188 struct wlr_event_pointer_motion_absolute *event = data;
186 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 189 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
187 cursor_send_pointer_motion(cursor, event->time_msec); 190 cursor_send_pointer_motion(cursor, event->time_msec);
@@ -231,6 +234,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
231 234
232static void handle_cursor_button(struct wl_listener *listener, void *data) { 235static void handle_cursor_button(struct wl_listener *listener, void *data) {
233 struct sway_cursor *cursor = wl_container_of(listener, cursor, button); 236 struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
237 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
234 struct wlr_event_pointer_button *event = data; 238 struct wlr_event_pointer_button *event = data;
235 dispatch_cursor_button(cursor, 239 dispatch_cursor_button(cursor,
236 event->time_msec, event->button, event->state); 240 event->time_msec, event->button, event->state);
@@ -238,6 +242,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
238 242
239static void handle_cursor_axis(struct wl_listener *listener, void *data) { 243static void handle_cursor_axis(struct wl_listener *listener, void *data) {
240 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); 244 struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
245 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
241 struct wlr_event_pointer_axis *event = data; 246 struct wlr_event_pointer_axis *event = data;
242 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 247 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
243 event->orientation, event->delta, event->delta_discrete, event->source); 248 event->orientation, event->delta, event->delta_discrete, event->source);
@@ -245,6 +250,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) {
245 250
246static void handle_touch_down(struct wl_listener *listener, void *data) { 251static void handle_touch_down(struct wl_listener *listener, void *data) {
247 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); 252 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
253 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
248 struct wlr_event_touch_down *event = data; 254 struct wlr_event_touch_down *event = data;
249 255
250 struct wlr_seat *seat = cursor->seat->wlr_seat; 256 struct wlr_seat *seat = cursor->seat->wlr_seat;
@@ -271,6 +277,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
271 277
272static void handle_touch_up(struct wl_listener *listener, void *data) { 278static void handle_touch_up(struct wl_listener *listener, void *data) {
273 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); 279 struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
280 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
274 struct wlr_event_touch_up *event = data; 281 struct wlr_event_touch_up *event = data;
275 struct wlr_seat *seat = cursor->seat->wlr_seat; 282 struct wlr_seat *seat = cursor->seat->wlr_seat;
276 // TODO: fall back to cursor simulation if client has not bound to touch 283 // TODO: fall back to cursor simulation if client has not bound to touch
@@ -280,6 +287,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
280static void handle_touch_motion(struct wl_listener *listener, void *data) { 287static void handle_touch_motion(struct wl_listener *listener, void *data) {
281 struct sway_cursor *cursor = 288 struct sway_cursor *cursor =
282 wl_container_of(listener, cursor, touch_motion); 289 wl_container_of(listener, cursor, touch_motion);
290 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
283 struct wlr_event_touch_motion *event = data; 291 struct wlr_event_touch_motion *event = data;
284 292
285 struct wlr_seat *seat = cursor->seat->wlr_seat; 293 struct wlr_seat *seat = cursor->seat->wlr_seat;
@@ -331,6 +339,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
331 339
332static void handle_tool_axis(struct wl_listener *listener, void *data) { 340static void handle_tool_axis(struct wl_listener *listener, void *data) {
333 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); 341 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
342 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
334 struct wlr_event_tablet_tool_axis *event = data; 343 struct wlr_event_tablet_tool_axis *event = data;
335 struct sway_input_device *input_device = event->device->data; 344 struct sway_input_device *input_device = event->device->data;
336 345
@@ -353,6 +362,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
353 362
354static void handle_tool_tip(struct wl_listener *listener, void *data) { 363static void handle_tool_tip(struct wl_listener *listener, void *data) {
355 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); 364 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
365 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
356 struct wlr_event_tablet_tool_tip *event = data; 366 struct wlr_event_tablet_tool_tip *event = data;
357 dispatch_cursor_button(cursor, event->time_msec, 367 dispatch_cursor_button(cursor, event->time_msec,
358 BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? 368 BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
@@ -361,6 +371,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
361 371
362static void handle_tool_button(struct wl_listener *listener, void *data) { 372static void handle_tool_button(struct wl_listener *listener, void *data) {
363 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); 373 struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
374 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
364 struct wlr_event_tablet_tool_button *event = data; 375 struct wlr_event_tablet_tool_button *event = data;
365 // TODO: the user may want to configure which tool buttons are mapped to 376 // TODO: the user may want to configure which tool buttons are mapped to
366 // which simulated pointer buttons 377 // which simulated pointer buttons
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index dbf2ce01..c07557db 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -2,6 +2,7 @@
2#include <limits.h> 2#include <limits.h>
3#include <wlr/backend/multi.h> 3#include <wlr/backend/multi.h>
4#include <wlr/backend/session.h> 4#include <wlr/backend/session.h>
5#include <wlr/types/wlr_idle.h>
5#include "sway/input/seat.h" 6#include "sway/input/seat.h"
6#include "sway/input/keyboard.h" 7#include "sway/input/keyboard.h"
7#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
@@ -330,6 +331,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
330 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; 331 struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
331 struct wlr_input_device *wlr_device = 332 struct wlr_input_device *wlr_device =
332 keyboard->seat_device->input_device->wlr_device; 333 keyboard->seat_device->input_device->wlr_device;
334 wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat);
333 struct wlr_event_keyboard_key *event = data; 335 struct wlr_event_keyboard_key *event = data;
334 336
335 xkb_keycode_t keycode = event->keycode + 8; 337 xkb_keycode_t keycode = event->keycode + 8;
diff --git a/sway/server.c b/sway/server.c
index 8c41ee87..ccc215eb 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -16,6 +16,7 @@
16#include <wlr/types/wlr_xcursor_manager.h> 16#include <wlr/types/wlr_xcursor_manager.h>
17#include <wlr/types/wlr_xdg_output.h> 17#include <wlr/types/wlr_xdg_output.h>
18#include <wlr/types/wlr_wl_shell.h> 18#include <wlr/types/wlr_wl_shell.h>
19#include <wlr/types/wlr_idle.h>
19#include <wlr/util/log.h> 20#include <wlr/util/log.h>
20// TODO WLR: make Xwayland optional 21// TODO WLR: make Xwayland optional
21#include <wlr/xwayland.h> 22#include <wlr/xwayland.h>
@@ -61,6 +62,7 @@ bool server_init(struct sway_server *server) {
61 server->data_device_manager = 62 server->data_device_manager =
62 wlr_data_device_manager_create(server->wl_display); 63 wlr_data_device_manager_create(server->wl_display);
63 64
65 server->idle = wlr_idle_create(server->wl_display);
64 wlr_screenshooter_create(server->wl_display); 66 wlr_screenshooter_create(server->wl_display);
65 wlr_gamma_control_manager_create(server->wl_display); 67 wlr_gamma_control_manager_create(server->wl_display);
66 wlr_primary_selection_device_manager_create(server->wl_display); 68 wlr_primary_selection_device_manager_create(server->wl_display);
diff --git a/swayidle/main.c b/swayidle/main.c
new file mode 100644
index 00000000..0eb9e202
--- /dev/null
+++ b/swayidle/main.c
@@ -0,0 +1,414 @@
1#define _XOPEN_SOURCE 500
2#include <getopt.h>
3#include <signal.h>
4#include <pthread.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <errno.h>
8#include <string.h>
9#include <unistd.h>
10#include <wayland-client-protocol.h>
11#include <wayland-client.h>
12#include <wayland-util.h>
13#include <wlr/config.h>
14#include <wlr/util/log.h>
15#include <wlr/types/wlr_output_layout.h>
16#include <wlr/types/wlr_output.h>
17#include "idle-client-protocol.h"
18#include "config.h"
19#include "list.h"
20#ifdef SWAY_IDLE_HAS_SYSTEMD
21#include <systemd/sd-bus.h>
22#include <systemd/sd-login.h>
23#elif defined(SWAY_IDLE_HAS_ELOGIND)
24#include <elogind/sd-bus.h>
25#include <elogind/sd-login.h>
26#endif
27
28typedef void (*timer_callback_func)(void *data);
29
30static struct org_kde_kwin_idle *idle_manager = NULL;
31static struct wl_seat *seat = NULL;
32bool debug = false;
33
34struct swayidle_state {
35 struct wl_display *display;
36 struct org_kde_kwin_idle_timeout *idle_timer;
37 struct org_kde_kwin_idle_timeout *lock_timer;
38 struct wlr_output_layout *layout;
39 struct wl_event_loop *event_loop;
40 list_t *timeout_cmds;
41} state;
42
43struct swayidle_cmd {
44 timer_callback_func callback;
45 char *param;
46};
47
48struct swayidle_cmd *lock_cmd = NULL;
49
50struct swayidle_timeout_cmd {
51 uint32_t timeout;
52 struct swayidle_cmd *idle_cmd;
53 struct swayidle_cmd *resume_cmd;
54};
55
56static void cmd_exec(void *data) {
57 if (data == NULL) {
58 return;
59 }
60 char *param = (char *)data;
61 wlr_log(L_DEBUG, "Cmd exec %s", param);
62 int pid = fork();
63 if (pid == 0) {
64 char *const cmd[] = { "sh", "-c", param, NULL, };
65 execvp(cmd[0], cmd);
66 exit(1);
67 }
68 wlr_log(L_DEBUG, "Spawned process %d", pid);
69}
70
71#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
72static int lock_fd = -1;
73static int ongoing_fd = -1;
74
75static int release_lock(void *data) {
76 wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd);
77 if (ongoing_fd >= 0) {
78 close(ongoing_fd);
79 }
80 ongoing_fd = -1;
81 return 0;
82}
83
84void acquire_sleep_lock() {
85 sd_bus_message *msg = NULL;
86 sd_bus_error error = SD_BUS_ERROR_NULL;
87 struct sd_bus *bus;
88 int ret = sd_bus_default_system(&bus);
89
90 if (ret < 0) {
91 wlr_log(L_ERROR, "Failed to open D-Bus connection: %s",
92 strerror(-ret));
93 return;
94 }
95
96 ret = sd_bus_call_method(bus, "org.freedesktop.login1",
97 "/org/freedesktop/login1",
98 "org.freedesktop.login1.Manager", "Inhibit",
99 &error, &msg, "ssss", "sleep", "swayidle",
100 "Setup Up Lock Screen", "delay");
101 if (ret < 0) {
102 wlr_log(L_ERROR, "Failed to send Inhibit signal: %s",
103 strerror(-ret));
104 } else {
105 ret = sd_bus_message_read(msg, "h", &lock_fd);
106 if (ret < 0) {
107 wlr_log(L_ERROR,
108 "Failed to parse D-Bus response for Inhibit: %s",
109 strerror(-ret));
110 }
111 }
112 wlr_log(L_INFO, "Got sleep lock: %d", lock_fd);
113}
114
115static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
116 sd_bus_error *ret_error) {
117 bool going_down = true;
118 int ret = sd_bus_message_read(msg, "b", &going_down);
119 if (ret < 0) {
120 wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
121 strerror(-ret));
122 }
123 wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down);
124 if (!going_down) {
125 acquire_sleep_lock();
126 return 0;
127 }
128
129 ongoing_fd = lock_fd;
130
131 if (lock_cmd && lock_cmd->callback) {
132 lock_cmd->callback(lock_cmd->param);
133 }
134
135 if (ongoing_fd >= 0) {
136 struct wl_event_source *source =
137 wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
138 wl_event_source_timer_update(source, 1000);
139 }
140 wlr_log(L_DEBUG, "Prepare for sleep done");
141 return 0;
142}
143
144static int dbus_event(int fd, uint32_t mask, void *data) {
145 sd_bus *bus = data;
146 while (sd_bus_process(bus, NULL) > 0) {
147 // Do nothing.
148 }
149 return 1;
150}
151
152void setup_sleep_listener() {
153 struct sd_bus *bus;
154
155 int ret = sd_bus_default_system(&bus);
156 if (ret < 0) {
157 wlr_log(L_ERROR, "Failed to open D-Bus connection: %s",
158 strerror(-ret));
159 return;
160 }
161
162 char str[256];
163 const char *fmt = "type='signal',"
164 "sender='org.freedesktop.login1',"
165 "interface='org.freedesktop.login1.%s',"
166 "member='%s'," "path='%s'";
167
168 snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep",
169 "/org/freedesktop/login1");
170 ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
171 if (ret < 0) {
172 wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
173 return;
174 }
175 acquire_sleep_lock();
176
177 wl_event_loop_add_fd(state.event_loop, sd_bus_get_fd(bus),
178 WL_EVENT_READABLE, dbus_event, bus);
179}
180#endif
181
182static void handle_global(void *data, struct wl_registry *registry,
183 uint32_t name, const char *interface, uint32_t version) {
184 if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) {
185 idle_manager =
186 wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
187 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
188 seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
189 }
190}
191
192static void handle_global_remove(void *data, struct wl_registry *registry,
193 uint32_t name) {
194}
195
196static const struct wl_registry_listener registry_listener = {
197 .global = handle_global,
198 .global_remove = handle_global_remove,
199};
200
201static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
202 struct swayidle_timeout_cmd *cmd = data;
203 wlr_log(L_DEBUG, "idle state");
204 if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) {
205 cmd->idle_cmd->callback(cmd->idle_cmd->param);
206 }
207}
208
209static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
210 struct swayidle_timeout_cmd *cmd = data;
211 wlr_log(L_DEBUG, "active state");
212 if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) {
213 cmd->resume_cmd->callback(cmd->resume_cmd->param);
214 }
215}
216
217static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
218 .idle = handle_idle,
219 .resumed = handle_resume,
220};
221
222struct swayidle_cmd *parse_command(int argc, char **argv) {
223 if (argc < 1) {
224 wlr_log(L_ERROR, "Too few parameters for command in parse_command");
225 return NULL;
226 }
227
228 struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd));
229 wlr_log(L_DEBUG, "Command: %s", argv[0]);
230 cmd->callback = cmd_exec;
231 cmd->param = argv[0];
232 return cmd;
233}
234
235int parse_timeout(int argc, char **argv) {
236 if (argc < 3) {
237 wlr_log(L_ERROR, "Too few parameters to timeout command. "
238 "Usage: timeout <seconds> <command>");
239 exit(-1);
240 }
241 errno = 0;
242 char *endptr;
243 int seconds = strtoul(argv[1], &endptr, 10);
244 if (errno != 0 || *endptr != '\0') {
245 wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a "
246 "numeric value representing seconds", optarg);
247 exit(-1);
248 }
249 struct swayidle_timeout_cmd *cmd =
250 calloc(1, sizeof(struct swayidle_timeout_cmd));
251 cmd->timeout = seconds * 1000;
252
253 wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
254 wlr_log(L_DEBUG, "Setup idle");
255 cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
256
257 int result = 3;
258 if (argc >= 5 && !strcmp("resume", argv[3])) {
259 wlr_log(L_DEBUG, "Setup resume");
260 cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
261 result = 5;
262 }
263 list_add(state.timeout_cmds, cmd);
264 return result;
265}
266
267int parse_sleep(int argc, char **argv) {
268 if (argc < 2) {
269 wlr_log(L_ERROR, "Too few parameters to before-sleep command. "
270 "Usage: before-sleep <command>");
271 exit(-1);
272 }
273
274 lock_cmd = parse_command(argc - 1, &argv[1]);
275 if (lock_cmd) {
276 wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param);
277 }
278
279 return 2;
280}
281
282
283int parse_args(int argc, char *argv[]) {
284 int c;
285
286 while ((c = getopt(argc, argv, "hs:d")) != -1) {
287 switch(c) {
288 case 'd':
289 debug = true;
290 break;
291 case 'h':
292 case '?':
293 printf("Usage: %s [OPTIONS]\n", argv[0]);
294 printf(" -d\tdebug\n");
295 printf(" -h\tthis help menu\n");
296 return 1;
297 default:
298 return 1;
299 }
300 }
301
302 if (debug) {
303 wlr_log_init(L_DEBUG, NULL);
304 wlr_log(L_DEBUG, "Loglevel debug");
305 } else {
306 wlr_log_init(L_INFO, NULL);
307 }
308
309
310 state.timeout_cmds = create_list();
311
312 int i = optind;
313 while (i < argc) {
314 if (!strcmp("timeout", argv[i])) {
315 wlr_log(L_DEBUG, "Got timeout");
316 i += parse_timeout(argc - i, &argv[i]);
317 } else if (!strcmp("before-sleep", argv[i])) {
318 wlr_log(L_DEBUG, "Got before-sleep");
319 i += parse_sleep(argc - i, &argv[i]);
320 } else {
321 wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]);
322 exit(-1);
323 }
324 }
325 return 0;
326}
327
328void sway_terminate(int exit_code) {
329 if (state.event_loop) {
330 wl_event_loop_destroy(state.event_loop);
331 }
332 if (state.display) {
333 wl_display_disconnect(state.display);
334 }
335 exit(exit_code);
336}
337
338void sig_handler(int signal) {
339 sway_terminate(0);
340}
341
342static int display_event(int fd, uint32_t mask, void *data) {
343 wl_display_dispatch(state.display);
344 return 0;
345}
346
347void register_idle_timeout(void *item) {
348 struct swayidle_timeout_cmd *cmd = item;
349 if (cmd == NULL || !cmd->timeout) {
350 wlr_log(L_ERROR, "Invalid idle cmd, will not register");
351 return;
352 }
353 state.idle_timer =
354 org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, cmd->timeout);
355 if (state.idle_timer != NULL) {
356 org_kde_kwin_idle_timeout_add_listener(state.idle_timer,
357 &idle_timer_listener, cmd);
358 } else {
359 wlr_log(L_ERROR, "Could not create idle timer");
360 }
361}
362
363int main(int argc, char *argv[]) {
364 signal(SIGINT, sig_handler);
365 signal(SIGTERM, sig_handler);
366
367 if (parse_args(argc, argv) != 0) {
368 return -1;
369 }
370
371 state.display = wl_display_connect(NULL);
372 if (state.display == NULL) {
373 wlr_log(L_ERROR, "Failed to create display");
374 return -3;
375 }
376
377 struct wl_registry *registry = wl_display_get_registry(state.display);
378 wl_registry_add_listener(registry, &registry_listener, NULL);
379 wl_display_roundtrip(state.display);
380 state.layout = wlr_output_layout_create();
381 state.event_loop = wl_event_loop_create();
382
383 if (idle_manager == NULL) {
384 wlr_log(L_ERROR, "Display doesn't support idle protocol");
385 return -4;
386 }
387 if (seat == NULL) {
388 wlr_log(L_ERROR, "Seat error");
389 return -5;
390 }
391
392 bool should_run = state.timeout_cmds->length > 0;
393#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
394 if (lock_cmd) {
395 should_run = true;
396 setup_sleep_listener();
397 }
398#endif
399 if (!should_run) {
400 wlr_log(L_INFO, "No command specified! Nothing to do, will exit");
401 sway_terminate(0);
402 }
403 list_foreach(state.timeout_cmds, register_idle_timeout);
404
405 wl_display_roundtrip(state.display);
406
407 wl_event_loop_add_fd(state.event_loop, wl_display_get_fd(state.display),
408 WL_EVENT_READABLE, display_event, NULL);
409
410 while (wl_event_loop_dispatch(state.event_loop, 0) != -1) {
411 ; //Intentionally left blank;
412 }
413 sway_terminate(0);
414}
diff --git a/swayidle/meson.build b/swayidle/meson.build
new file mode 100644
index 00000000..09c482d6
--- /dev/null
+++ b/swayidle/meson.build
@@ -0,0 +1,17 @@
1threads = dependency('threads')
2
3executable(
4 'swayidle', [
5 'main.c',
6 ],
7 include_directories: [sway_inc],
8 dependencies: [
9 client_protos,
10 wayland_client,
11 wayland_server,
12 wlroots,
13 swayidle_deps,
14 ],
15 link_with: [lib_sway_common, lib_sway_client],
16 install: true
17)
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd
new file mode 100644
index 00000000..5cd4a7fd
--- /dev/null
+++ b/swayidle/swayidle.1.scd
@@ -0,0 +1,61 @@
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
25# EVENTS
26
27*timeout* <timeout> <timeout command> [resume <resume command>]
28 Execute _timeout command_ if there is no activity for <timeout> seconds.
29
30 If you specify "resume <resume command>", _resume command_ will be run when
31 there is activity again.
32
33*before-sleep* <command>
34 If built with systemd support, executes _command_ before systemd puts the
35 computer to sleep.
36
37All commands are executed in a shell.
38
39# EXAMPLE
40
41```
42 swayidle \
43 timeout 300 'swaylock -c 000000' \
44 timeout 600 'swaymsg "output * dpms off"' \
45 resume 'swaymsg "output * dpms on"' \
46 before-sleep 'swaylock -c 000000'
47```
48
49This will lock your screen after 300 seconds of inactivity, then turn off your
50displays after another 600 seconds, and turn your screens back on when resumed.
51It will also lock your screen before your computer goes to sleep.
52
53# AUTHORS
54
55Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
56source contributors. For more information about sway development, see
57https://github.com/swaywm/sway.
58
59# SEE ALSO
60
61*sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5)
diff --git a/swayidle/swayidle.1.txt b/swayidle/swayidle.1.txt
new file mode 100644
index 00000000..a32e6fd5
--- /dev/null
+++ b/swayidle/swayidle.1.txt
@@ -0,0 +1,67 @@
1/////
2vim:set ts=4 sw=4 tw=82 noet:
3/////
4:quotes.~:
5
6swayidle (1)
7============
8
9Name
10----
11swayidle - Idle manager for Wayland
12
13Synopsis
14--------
15'swayidle' [options] [events...]
16
17Options
18-------
19
20*-h*::
21 Show help message and quit.
22
23*-d*::
24 Enable debug output.
25
26Description
27-----------
28
29swayidle listens for idle activity on your Wayland compositor and executes tasks
30on various idle-related events. You can specify any number of events at the
31command line.
32
33Events
34------
35
36*timeout* <timeout> <timeout command> [resume <resume command>]::
37 Execute <timeout command> if there is no activity for <timeout> seconds.
38 +
39 If you specify "resume <resume command>", <resume command> will be run when
40 there is activity again.
41
42*before-sleep* <command>::
43 If built with systemd support, executes <command> before systemd puts the
44 computer to sleep.
45
46All commands are executed in a shell.
47
48Example
49-------
50
51 swayidle \
52 timeout 300 'swaylock -c 000000' \
53 timeout 600 'swaymsg "output * dpms off"' \
54 resume 'swaymsg "output * dpms on"' \
55 before-sleep 'swaylock -c 000000'
56
57This will lock your screen after 300 seconds of inactivity, then turn off your
58displays after another 600 seconds, and turn your screens back on when resumed.
59It will also lock your screen before your computer goes to sleep.
60
61Authors
62-------
63
64Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
65source contributors. For more information about sway development, see
66<https://github.com/swaywm/sway>.
67