Merge pull request #1824 from snaggen/idle

DPMS and lock handling
This commit is contained in:
Drew DeVault 2018-05-13 10:11:10 -04:00 committed by GitHub
commit 61aa0937a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 636 additions and 2 deletions

View file

@ -29,6 +29,20 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
# #
# You can get the names of your outputs by running: swaymsg -t get_outputs # You can get the names of your outputs by running: swaymsg -t get_outputs
### Idle configuration
#
# Example configuration:
#
#exec swayidle \
# timeout 300 'swaylock -c 000000' \
# timeout 600 'swaymsg "output * dpms off"' \
# resume 'swaymsg "output * dpms on"' \
# before-sleep 'swaylock -c 000000'
#
# This will lock your screen after 300 seconds of inactivity, then turn off
# your displays after another 600 seconds, and turn your screens back on when
# resumed. It will also lock your screen before your computer goes to sleep.
### Input configuration ### Input configuration
# #
# Example configuration: # Example configuration:

View file

@ -107,6 +107,12 @@ struct seat_config {
list_t *attachments; // list of seat_attachment configs list_t *attachments; // list of seat_attachment configs
}; };
enum config_dpms {
DPMS_IGNORE,
DPMS_ON,
DPMS_OFF
};
/** /**
* Size and position configuration for a particular output. * Size and position configuration for a particular output.
* *
@ -123,6 +129,7 @@ struct output_config {
char *background; char *background;
char *background_option; char *background_option;
enum config_dpms dpms_state;
}; };
/** /**

View file

@ -21,6 +21,7 @@ struct sway_server {
struct wlr_compositor *compositor; struct wlr_compositor *compositor;
struct wlr_data_device_manager *data_device_manager; struct wlr_data_device_manager *data_device_manager;
struct wlr_idle *idle;
struct sway_input_manager *input; struct sway_input_manager *input;

View file

@ -20,6 +20,8 @@ datadir = get_option('datadir')
sysconfdir = get_option('sysconfdir') sysconfdir = get_option('sysconfdir')
prefix = get_option('prefix') prefix = get_option('prefix')
swayidle_deps = []
jsonc = dependency('json-c', version: '>=0.13') jsonc = dependency('json-c', version: '>=0.13')
pcre = dependency('libpcre') pcre = dependency('libpcre')
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
@ -37,6 +39,8 @@ pixman = dependency('pixman-1')
libcap = dependency('libcap') libcap = dependency('libcap')
libinput = dependency('libinput', version: '>=1.6.0') libinput = dependency('libinput', version: '>=1.6.0')
libpam = cc.find_library('pam') libpam = cc.find_library('pam')
systemd = dependency('libsystemd', required: false)
elogind = dependency('libelogind', required: false)
math = cc.find_library('m') math = cc.find_library('m')
rt = cc.find_library('rt') rt = cc.find_library('rt')
git = find_program('git', required: false) git = find_program('git', required: false)
@ -47,6 +51,16 @@ if gdk_pixbuf.found()
conf_data.set('HAVE_GDK_PIXBUF', true) conf_data.set('HAVE_GDK_PIXBUF', true)
endif endif
if systemd.found()
conf_data.set('SWAY_IDLE_HAS_SYSTEMD', true)
swayidle_deps += systemd
endif
if elogind.found()
conf_data.set('SWAY_IDLE_HAS_ELOGIND', true)
swayidle_deps += elogind
endif
scdoc = find_program('scdoc', required: false) scdoc = find_program('scdoc', required: false)
if scdoc.found() if scdoc.found()
@ -59,6 +73,7 @@ if scdoc.found()
'sway/sway-input.5.scd', 'sway/sway-input.5.scd',
'swaylock/swaylock.1.scd', 'swaylock/swaylock.1.scd',
'swaymsg/swaymsg.1.scd', 'swaymsg/swaymsg.1.scd',
'swayidle/swayidle.1.scd',
] ]
foreach filename : man_files foreach filename : man_files
topic = filename.split('.')[-3].split('/')[-1] topic = filename.split('.')[-3].split('/')[-1]
@ -106,6 +121,7 @@ subdir('client')
subdir('swaybg') subdir('swaybg')
subdir('swaybar') subdir('swaybar')
subdir('swaylock') subdir('swaylock')
subdir('swayidle')
config = configuration_data() config = configuration_data()
config.set('sysconfdir', join_paths(prefix, sysconfdir)) config.set('sysconfdir', join_paths(prefix, sysconfdir))

49
protocols/idle.xml Normal file
View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle">
<copyright><![CDATA[
Copyright (C) 2015 Martin Gräßlin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]></copyright>
<interface name="org_kde_kwin_idle" version="1">
<description summary="User idle time manager">
This interface allows to monitor user idle time on a given seat. The interface
allows to register timers which trigger after no user activity was registered
on the seat for a given interval. It notifies when user activity resumes.
This is useful for applications wanting to perform actions when the user is not
interacting with the system, e.g. chat applications setting the user as away, power
management features to dim screen, etc..
</description>
<request name="get_idle_timeout">
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
</request>
</interface>
<interface name="org_kde_kwin_idle_timeout" version="1">
<request name="release" type="destructor">
<description summary="release the timeout object"/>
</request>
<request name="simulate_user_activity">
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
</request>
<event name="idle">
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
</event>
<event name="resumed">
<description summary="Triggered on the first user activity after an idle event"/>
</event>
</interface>
</protocol>

View file

@ -30,6 +30,7 @@ wayland_scanner_server = generator(
client_protocols = [ client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
['wlr-layer-shell-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'],
['idle.xml'],
['wlr-input-inhibitor-unstable-v1.xml'] ['wlr-input-inhibitor-unstable-v1.xml']
] ]

View file

@ -20,6 +20,25 @@ static char *bg_options[] = {
"tile", "tile",
}; };
static struct cmd_results *cmd_output_dpms(struct output_config *output,
int *i, int argc, char **argv) {
if (++*i >= argc) {
return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument.");
}
char *value = argv[*i];
if (strcmp(value, "on") == 0) {
output->dpms_state = DPMS_ON;
} else if (strcmp(value, "off") == 0) {
output->dpms_state = DPMS_OFF;
} else {
return cmd_results_new(CMD_INVALID, "output",
"Invalid dpms state, valid states are on/off.");
}
return NULL;
}
static struct cmd_results *cmd_output_mode(struct output_config *output, static struct cmd_results *cmd_output_mode(struct output_config *output,
int *i, int argc, char **argv) { int *i, int argc, char **argv) {
if (++*i >= argc) { if (++*i >= argc) {
@ -263,6 +282,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
} else if (strcasecmp(command, "background") == 0 || } else if (strcasecmp(command, "background") == 0 ||
strcasecmp(command, "bg") == 0) { strcasecmp(command, "bg") == 0) {
error = cmd_output_background(output, &i, argc, argv); error = cmd_output_background(output, &i, argc, argv);
} else if (strcasecmp(command, "dpms") == 0) {
error = cmd_output_dpms(output, &i, argc, argv);
} else { } else {
error = cmd_results_new(CMD_INVALID, "output", error = cmd_results_new(CMD_INVALID, "output",
"Invalid output subcommand: %s.", command); "Invalid output subcommand: %s.", command);
@ -285,10 +306,10 @@ struct cmd_results *cmd_output(int argc, char **argv) {
} }
wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f transform %d) (bg %s %s)", "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)",
output->name, output->enabled, output->width, output->height, output->name, output->enabled, output->width, output->height,
output->refresh_rate, output->x, output->y, output->scale, output->refresh_rate, output->x, output->y, output->scale,
output->transform, output->background, output->background_option); output->transform, output->background, output->background_option, output->dpms_state);
// Try to find the output container and apply configuration now. If // Try to find the output container and apply configuration now. If
// this is during startup then there will be no container and config // this is during startup then there will be no container and config

View file

@ -165,6 +165,7 @@ static void config_defaults(struct sway_config *config) {
config->floating_mod = 0; config->floating_mod = 0;
config->dragging_key = BTN_LEFT; config->dragging_key = BTN_LEFT;
config->resizing_key = BTN_RIGHT; config->resizing_key = BTN_RIGHT;
if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup;
if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup;

View file

@ -81,6 +81,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
free(dst->background_option); free(dst->background_option);
dst->background_option = strdup(src->background_option); dst->background_option = strdup(src->background_option);
} }
if (src->dpms_state != 0) {
dst->dpms_state = src->dpms_state;
}
} }
static void set_mode(struct wlr_output *output, int width, int height, static 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
execvp(cmd[0], cmd); execvp(cmd[0], cmd);
} }
} }
if (oc && oc->dpms_state != DPMS_IGNORE) {
switch (oc->dpms_state) {
case DPMS_ON:
wlr_log(L_DEBUG, "Turning on screen");
wlr_output_enable(wlr_output, true);
break;
case DPMS_OFF:
wlr_log(L_DEBUG, "Turning off screen");
wlr_output_enable(wlr_output, false);
break;
case DPMS_IGNORE:
break;
}
}
} }
void free_output_config(struct output_config *oc) { void free_output_config(struct output_config *oc) {

View file

@ -7,6 +7,7 @@
#endif #endif
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_idle.h>
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
#include "sway/input/cursor.h" #include "sway/input/cursor.h"
@ -172,6 +173,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec)
static void handle_cursor_motion(struct wl_listener *listener, void *data) { static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_pointer_motion *event = data; struct wlr_event_pointer_motion *event = data;
wlr_cursor_move(cursor->cursor, event->device, wlr_cursor_move(cursor->cursor, event->device,
event->delta_x, event->delta_y); event->delta_x, event->delta_y);
@ -182,6 +184,7 @@ static void handle_cursor_motion_absolute(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = struct sway_cursor *cursor =
wl_container_of(listener, cursor, motion_absolute); wl_container_of(listener, cursor, motion_absolute);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_pointer_motion_absolute *event = data; struct wlr_event_pointer_motion_absolute *event = data;
wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
cursor_send_pointer_motion(cursor, event->time_msec); cursor_send_pointer_motion(cursor, event->time_msec);
@ -231,6 +234,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
static void handle_cursor_button(struct wl_listener *listener, void *data) { static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_pointer_button *event = data; struct wlr_event_pointer_button *event = data;
dispatch_cursor_button(cursor, dispatch_cursor_button(cursor,
event->time_msec, event->button, event->state); event->time_msec, event->button, event->state);
@ -238,6 +242,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
static void handle_cursor_axis(struct wl_listener *listener, void *data) { static void handle_cursor_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_pointer_axis *event = data; struct wlr_event_pointer_axis *event = data;
wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
event->orientation, event->delta, event->delta_discrete, event->source); event->orientation, event->delta, event->delta_discrete, event->source);
@ -245,6 +250,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) {
static void handle_touch_down(struct wl_listener *listener, void *data) { static void handle_touch_down(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_touch_down *event = data; struct wlr_event_touch_down *event = data;
struct wlr_seat *seat = cursor->seat->wlr_seat; struct wlr_seat *seat = cursor->seat->wlr_seat;
@ -271,6 +277,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
static void handle_touch_up(struct wl_listener *listener, void *data) { static void handle_touch_up(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_touch_up *event = data; struct wlr_event_touch_up *event = data;
struct wlr_seat *seat = cursor->seat->wlr_seat; struct wlr_seat *seat = cursor->seat->wlr_seat;
// TODO: fall back to cursor simulation if client has not bound to touch // 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) {
static void handle_touch_motion(struct wl_listener *listener, void *data) { static void handle_touch_motion(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = struct sway_cursor *cursor =
wl_container_of(listener, cursor, touch_motion); wl_container_of(listener, cursor, touch_motion);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_touch_motion *event = data; struct wlr_event_touch_motion *event = data;
struct wlr_seat *seat = cursor->seat->wlr_seat; struct wlr_seat *seat = cursor->seat->wlr_seat;
@ -331,6 +339,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
static void handle_tool_axis(struct wl_listener *listener, void *data) { static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_tablet_tool_axis *event = data; struct wlr_event_tablet_tool_axis *event = data;
struct sway_input_device *input_device = event->device->data; struct sway_input_device *input_device = event->device->data;
@ -353,6 +362,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
static void handle_tool_tip(struct wl_listener *listener, void *data) { static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_tablet_tool_tip *event = data; struct wlr_event_tablet_tool_tip *event = data;
dispatch_cursor_button(cursor, event->time_msec, dispatch_cursor_button(cursor, event->time_msec,
BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ?
@ -361,6 +371,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
static void handle_tool_button(struct wl_listener *listener, void *data) { static void handle_tool_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
struct wlr_event_tablet_tool_button *event = data; struct wlr_event_tablet_tool_button *event = data;
// TODO: the user may want to configure which tool buttons are mapped to // TODO: the user may want to configure which tool buttons are mapped to
// which simulated pointer buttons // which simulated pointer buttons

View file

@ -2,6 +2,7 @@
#include <limits.h> #include <limits.h>
#include <wlr/backend/multi.h> #include <wlr/backend/multi.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/types/wlr_idle.h>
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/input/keyboard.h" #include "sway/input/keyboard.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
@ -330,6 +331,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
struct wlr_input_device *wlr_device = struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device; keyboard->seat_device->input_device->wlr_device;
wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat);
struct wlr_event_keyboard_key *event = data; struct wlr_event_keyboard_key *event = data;
xkb_keycode_t keycode = event->keycode + 8; xkb_keycode_t keycode = event->keycode + 8;

View file

@ -16,6 +16,7 @@
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_output.h> #include <wlr/types/wlr_xdg_output.h>
#include <wlr/types/wlr_wl_shell.h> #include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
// TODO WLR: make Xwayland optional // TODO WLR: make Xwayland optional
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
@ -61,6 +62,7 @@ bool server_init(struct sway_server *server) {
server->data_device_manager = server->data_device_manager =
wlr_data_device_manager_create(server->wl_display); wlr_data_device_manager_create(server->wl_display);
server->idle = wlr_idle_create(server->wl_display);
wlr_screenshooter_create(server->wl_display); wlr_screenshooter_create(server->wl_display);
wlr_gamma_control_manager_create(server->wl_display); wlr_gamma_control_manager_create(server->wl_display);
wlr_primary_selection_device_manager_create(server->wl_display); wlr_primary_selection_device_manager_create(server->wl_display);

414
swayidle/main.c Normal file
View file

@ -0,0 +1,414 @@
#define _XOPEN_SOURCE 500
#include <getopt.h>
#include <signal.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include <wayland-util.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include "idle-client-protocol.h"
#include "config.h"
#include "list.h"
#ifdef SWAY_IDLE_HAS_SYSTEMD
#include <systemd/sd-bus.h>
#include <systemd/sd-login.h>
#elif defined(SWAY_IDLE_HAS_ELOGIND)
#include <elogind/sd-bus.h>
#include <elogind/sd-login.h>
#endif
typedef void (*timer_callback_func)(void *data);
static struct org_kde_kwin_idle *idle_manager = NULL;
static struct wl_seat *seat = NULL;
bool debug = false;
struct swayidle_state {
struct wl_display *display;
struct org_kde_kwin_idle_timeout *idle_timer;
struct org_kde_kwin_idle_timeout *lock_timer;
struct wlr_output_layout *layout;
struct wl_event_loop *event_loop;
list_t *timeout_cmds;
} state;
struct swayidle_cmd {
timer_callback_func callback;
char *param;
};
struct swayidle_cmd *lock_cmd = NULL;
struct swayidle_timeout_cmd {
uint32_t timeout;
struct swayidle_cmd *idle_cmd;
struct swayidle_cmd *resume_cmd;
};
static void cmd_exec(void *data) {
if (data == NULL) {
return;
}
char *param = (char *)data;
wlr_log(L_DEBUG, "Cmd exec %s", param);
int pid = fork();
if (pid == 0) {
char *const cmd[] = { "sh", "-c", param, NULL, };
execvp(cmd[0], cmd);
exit(1);
}
wlr_log(L_DEBUG, "Spawned process %d", pid);
}
#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
static int lock_fd = -1;
static int ongoing_fd = -1;
static int release_lock(void *data) {
wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd);
if (ongoing_fd >= 0) {
close(ongoing_fd);
}
ongoing_fd = -1;
return 0;
}
void acquire_sleep_lock() {
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
struct sd_bus *bus;
int ret = sd_bus_default_system(&bus);
if (ret < 0) {
wlr_log(L_ERROR, "Failed to open D-Bus connection: %s",
strerror(-ret));
return;
}
ret = sd_bus_call_method(bus, "org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager", "Inhibit",
&error, &msg, "ssss", "sleep", "swayidle",
"Setup Up Lock Screen", "delay");
if (ret < 0) {
wlr_log(L_ERROR, "Failed to send Inhibit signal: %s",
strerror(-ret));
} else {
ret = sd_bus_message_read(msg, "h", &lock_fd);
if (ret < 0) {
wlr_log(L_ERROR,
"Failed to parse D-Bus response for Inhibit: %s",
strerror(-ret));
}
}
wlr_log(L_INFO, "Got sleep lock: %d", lock_fd);
}
static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
bool going_down = true;
int ret = sd_bus_message_read(msg, "b", &going_down);
if (ret < 0) {
wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
strerror(-ret));
}
wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down);
if (!going_down) {
acquire_sleep_lock();
return 0;
}
ongoing_fd = lock_fd;
if (lock_cmd && lock_cmd->callback) {
lock_cmd->callback(lock_cmd->param);
}
if (ongoing_fd >= 0) {
struct wl_event_source *source =
wl_event_loop_add_timer(state.event_loop, release_lock, NULL);
wl_event_source_timer_update(source, 1000);
}
wlr_log(L_DEBUG, "Prepare for sleep done");
return 0;
}
static int dbus_event(int fd, uint32_t mask, void *data) {
sd_bus *bus = data;
while (sd_bus_process(bus, NULL) > 0) {
// Do nothing.
}
return 1;
}
void setup_sleep_listener() {
struct sd_bus *bus;
int ret = sd_bus_default_system(&bus);
if (ret < 0) {
wlr_log(L_ERROR, "Failed to open D-Bus connection: %s",
strerror(-ret));
return;
}
char str[256];
const char *fmt = "type='signal',"
"sender='org.freedesktop.login1',"
"interface='org.freedesktop.login1.%s',"
"member='%s'," "path='%s'";
snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep",
"/org/freedesktop/login1");
ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL);
if (ret < 0) {
wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return;
}
acquire_sleep_lock();
wl_event_loop_add_fd(state.event_loop, sd_bus_get_fd(bus),
WL_EVENT_READABLE, dbus_event, bus);
}
#endif
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) {
idle_manager =
wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
}
}
static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
struct swayidle_timeout_cmd *cmd = data;
wlr_log(L_DEBUG, "idle state");
if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) {
cmd->idle_cmd->callback(cmd->idle_cmd->param);
}
}
static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
struct swayidle_timeout_cmd *cmd = data;
wlr_log(L_DEBUG, "active state");
if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) {
cmd->resume_cmd->callback(cmd->resume_cmd->param);
}
}
static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
.idle = handle_idle,
.resumed = handle_resume,
};
struct swayidle_cmd *parse_command(int argc, char **argv) {
if (argc < 1) {
wlr_log(L_ERROR, "Too few parameters for command in parse_command");
return NULL;
}
struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd));
wlr_log(L_DEBUG, "Command: %s", argv[0]);
cmd->callback = cmd_exec;
cmd->param = argv[0];
return cmd;
}
int parse_timeout(int argc, char **argv) {
if (argc < 3) {
wlr_log(L_ERROR, "Too few parameters to timeout command. "
"Usage: timeout <seconds> <command>");
exit(-1);
}
errno = 0;
char *endptr;
int seconds = strtoul(argv[1], &endptr, 10);
if (errno != 0 || *endptr != '\0') {
wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a "
"numeric value representing seconds", optarg);
exit(-1);
}
struct swayidle_timeout_cmd *cmd =
calloc(1, sizeof(struct swayidle_timeout_cmd));
cmd->timeout = seconds * 1000;
wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
wlr_log(L_DEBUG, "Setup idle");
cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
int result = 3;
if (argc >= 5 && !strcmp("resume", argv[3])) {
wlr_log(L_DEBUG, "Setup resume");
cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
result = 5;
}
list_add(state.timeout_cmds, cmd);
return result;
}
int parse_sleep(int argc, char **argv) {
if (argc < 2) {
wlr_log(L_ERROR, "Too few parameters to before-sleep command. "
"Usage: before-sleep <command>");
exit(-1);
}
lock_cmd = parse_command(argc - 1, &argv[1]);
if (lock_cmd) {
wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param);
}
return 2;
}
int parse_args(int argc, char *argv[]) {
int c;
while ((c = getopt(argc, argv, "hs:d")) != -1) {
switch(c) {
case 'd':
debug = true;
break;
case 'h':
case '?':
printf("Usage: %s [OPTIONS]\n", argv[0]);
printf(" -d\tdebug\n");
printf(" -h\tthis help menu\n");
return 1;
default:
return 1;
}
}
if (debug) {
wlr_log_init(L_DEBUG, NULL);
wlr_log(L_DEBUG, "Loglevel debug");
} else {
wlr_log_init(L_INFO, NULL);
}
state.timeout_cmds = create_list();
int i = optind;
while (i < argc) {
if (!strcmp("timeout", argv[i])) {
wlr_log(L_DEBUG, "Got timeout");
i += parse_timeout(argc - i, &argv[i]);
} else if (!strcmp("before-sleep", argv[i])) {
wlr_log(L_DEBUG, "Got before-sleep");
i += parse_sleep(argc - i, &argv[i]);
} else {
wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]);
exit(-1);
}
}
return 0;
}
void sway_terminate(int exit_code) {
if (state.event_loop) {
wl_event_loop_destroy(state.event_loop);
}
if (state.display) {
wl_display_disconnect(state.display);
}
exit(exit_code);
}
void sig_handler(int signal) {
sway_terminate(0);
}
static int display_event(int fd, uint32_t mask, void *data) {
wl_display_dispatch(state.display);
return 0;
}
void register_idle_timeout(void *item) {
struct swayidle_timeout_cmd *cmd = item;
if (cmd == NULL || !cmd->timeout) {
wlr_log(L_ERROR, "Invalid idle cmd, will not register");
return;
}
state.idle_timer =
org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, cmd->timeout);
if (state.idle_timer != NULL) {
org_kde_kwin_idle_timeout_add_listener(state.idle_timer,
&idle_timer_listener, cmd);
} else {
wlr_log(L_ERROR, "Could not create idle timer");
}
}
int main(int argc, char *argv[]) {
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
if (parse_args(argc, argv) != 0) {
return -1;
}
state.display = wl_display_connect(NULL);
if (state.display == NULL) {
wlr_log(L_ERROR, "Failed to create display");
return -3;
}
struct wl_registry *registry = wl_display_get_registry(state.display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(state.display);
state.layout = wlr_output_layout_create();
state.event_loop = wl_event_loop_create();
if (idle_manager == NULL) {
wlr_log(L_ERROR, "Display doesn't support idle protocol");
return -4;
}
if (seat == NULL) {
wlr_log(L_ERROR, "Seat error");
return -5;
}
bool should_run = state.timeout_cmds->length > 0;
#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND)
if (lock_cmd) {
should_run = true;
setup_sleep_listener();
}
#endif
if (!should_run) {
wlr_log(L_INFO, "No command specified! Nothing to do, will exit");
sway_terminate(0);
}
list_foreach(state.timeout_cmds, register_idle_timeout);
wl_display_roundtrip(state.display);
wl_event_loop_add_fd(state.event_loop, wl_display_get_fd(state.display),
WL_EVENT_READABLE, display_event, NULL);
while (wl_event_loop_dispatch(state.event_loop, 0) != -1) {
; //Intentionally left blank;
}
sway_terminate(0);
}

17
swayidle/meson.build Normal file
View file

@ -0,0 +1,17 @@
threads = dependency('threads')
executable(
'swayidle', [
'main.c',
],
include_directories: [sway_inc],
dependencies: [
client_protos,
wayland_client,
wayland_server,
wlroots,
swayidle_deps,
],
link_with: [lib_sway_common, lib_sway_client],
install: true
)

61
swayidle/swayidle.1.scd Normal file
View file

@ -0,0 +1,61 @@
swayidle (1)
# NAME
swayidle - Idle manager for Wayland
# SYNOPSIS
*swayidle* [options] [events...]
# OPTIONS
*-h*
Show help message and quit.
*-d*
Enable debug output.
# DESCRIPTION
swayidle listens for idle activity on your Wayland compositor and executes tasks
on various idle-related events. You can specify any number of events at the
command line.
# EVENTS
*timeout* <timeout> <timeout command> [resume <resume command>]
Execute _timeout command_ if there is no activity for <timeout> seconds.
If you specify "resume <resume command>", _resume command_ will be run when
there is activity again.
*before-sleep* <command>
If built with systemd support, executes _command_ before systemd puts the
computer to sleep.
All commands are executed in a shell.
# EXAMPLE
```
swayidle \
timeout 300 'swaylock -c 000000' \
timeout 600 'swaymsg "output * dpms off"' \
resume 'swaymsg "output * dpms on"' \
before-sleep 'swaylock -c 000000'
```
This will lock your screen after 300 seconds of inactivity, then turn off your
displays after another 600 seconds, and turn your screens back on when resumed.
It will also lock your screen before your computer goes to sleep.
# AUTHORS
Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
source contributors. For more information about sway development, see
https://github.com/swaywm/sway.
# SEE ALSO
*sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5)