Split swayidle from sway

This commit is contained in:
Drew DeVault 2019-01-12 09:40:09 -05:00 committed by Brian Ashworth
parent d256182f49
commit bc808680b1
7 changed files with 0 additions and 650 deletions

View file

@ -1,48 +0,0 @@
# swaymsg(1) completion
_swayidle()
{
local cur prev
_get_comp_words_by_ref -n : cur prev
local prev2=${COMP_WORDS[COMP_CWORD-2]}
local prev3=${COMP_WORDS[COMP_CWORD-3]}
events=(
'timeout'
'before-sleep'
)
short=(
-h
-d
)
if [ "$prev" = timeout ]; then
# timeout <timeout>
return
elif [ "$prev2" = timeout ]; then
# timeout <timeout> <timeout command>
COMPREPLY=($(compgen -c -- "$cur"))
return
elif [ "$prev3" = timeout ]; then
# timeout <timeout> <timeout command> [resume <resume command>]
COMPREPLY=(resume)
# optional argument; no return here as user may skip 'resume'
fi
case "$prev" in
resume)
COMPREPLY=($(compgen -c -- "$cur"))
return
;;
before-sleep)
COMPREPLY=($(compgen -c -- "$cur"))
return
;;
esac
COMPREPLY+=($(compgen -W "${events[*]}" -- "$cur"))
COMPREPLY+=($(compgen -W "${short[*]}" -- "$cur"))
} &&
complete -F _swayidle swayidle

View file

@ -1,3 +0,0 @@
# swayidle
complete -c swayidle -s h --description 'show help'
complete -c swayidle -s d --description 'debug'

View file

@ -1,22 +0,0 @@
#compdef swayidle
#
# Completion script for swayidle
#
local events=('timeout:Execute timeout command if there is no activity for timeout seconds'
'before-sleep:Execute before-sleep command before sleep')
local resume=('resume:Execute command when there is activity again')
if (($#words <= 2)); then
_arguments -C \
'(-h --help)'{-h,--help}'[Show help message and quit]' \
'(-d)'-d'[Enable debug output]'
_describe -t "events" 'swayidle' events
elif [[ "$words[-3]" == before-sleep || "$words[-3]" == resume ]]; then
_describe -t "events" 'swayidle' events
elif [[ "$words[-4]" == timeout ]]; then
_describe -t "events" 'swayidle' events
_describe -t "resume" 'swayidle' resume
fi

View file

@ -86,7 +86,6 @@ if scdoc.found()
'sway/sway-output.5.scd',
'swaylock/swaylock.1.scd',
'swaymsg/swaymsg.1.scd',
'swayidle/swayidle.1.scd',
'swaynag/swaynag.1.scd',
'swaynag/swaynag.5.scd',
]
@ -146,7 +145,6 @@ subdir('swaymsg')
subdir('client')
subdir('swaybg')
subdir('swaybar')
subdir('swayidle')
subdir('swaynag')
subdir('swaylock')
@ -214,7 +212,6 @@ if (get_option('bash-completions'))
bash_files = files(
'completions/bash/sway',
'completions/bash/swaybar',
'completions/bash/swayidle',
'completions/bash/swaylock',
'completions/bash/swaymsg',
)
@ -226,7 +223,6 @@ endif
if (get_option('fish-completions'))
fish_files = files(
'completions/fish/sway.fish',
'completions/fish/swayidle.fish',
'completions/fish/swaylock.fish',
'completions/fish/swaymsg.fish',
'completions/fish/swaynag.fish',

View file

@ -1,483 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include "config.h"
#include "idle-client-protocol.h"
#include "list.h"
#if HAVE_SYSTEMD
#include <systemd/sd-bus.h>
#include <systemd/sd-login.h>
#elif HAVE_ELOGIND
#include <elogind/sd-bus.h>
#include <elogind/sd-login.h>
#endif
static struct org_kde_kwin_idle *idle_manager = NULL;
static struct wl_seat *seat = NULL;
struct swayidle_state {
struct wl_display *display;
struct wl_event_loop *event_loop;
list_t *timeout_cmds; // struct swayidle_timeout_cmd *
char *lock_cmd;
} state;
struct swayidle_timeout_cmd {
int timeout, registered_timeout;
struct org_kde_kwin_idle_timeout *idle_timer;
char *idle_cmd;
char *resume_cmd;
};
void sway_terminate(int exit_code) {
wl_display_disconnect(state.display);
wl_event_loop_destroy(state.event_loop);
exit(exit_code);
}
static void cmd_exec(char *param) {
wlr_log(WLR_DEBUG, "Cmd exec %s", param);
pid_t pid = fork();
if (pid == 0) {
pid = fork();
if (pid == 0) {
char *const cmd[] = { "sh", "-c", param, NULL, };
execvp(cmd[0], cmd);
wlr_log_errno(WLR_ERROR, "execve failed!");
exit(1);
} else if (pid < 0) {
wlr_log_errno(WLR_ERROR, "fork failed");
exit(1);
}
exit(0);
} else if (pid < 0) {
wlr_log_errno(WLR_ERROR, "fork failed");
} else {
wlr_log(WLR_DEBUG, "Spawned process %s", param);
waitpid(pid, NULL, 0);
}
}
#if HAVE_SYSTEMD || HAVE_ELOGIND
static int lock_fd = -1;
static int ongoing_fd = -1;
static struct sd_bus *bus = NULL;
static int release_lock(void *data) {
wlr_log(WLR_INFO, "Releasing sleep lock %d", ongoing_fd);
if (ongoing_fd >= 0) {
close(ongoing_fd);
}
ongoing_fd = -1;
return 0;
}
static void acquire_sleep_lock(void) {
sd_bus_message *msg = NULL;
sd_bus_error error = SD_BUS_ERROR_NULL;
int 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(WLR_ERROR, "Failed to send Inhibit signal: %s", error.message);
sd_bus_error_free(&error);
return;
}
ret = sd_bus_message_read(msg, "h", &lock_fd);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
strerror(-ret));
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
return;
} else {
wlr_log(WLR_INFO, "Got sleep lock: %d", lock_fd);
}
// sd_bus_message_unref closes the file descriptor so we need
// to copy it beforehand
lock_fd = fcntl(lock_fd, F_DUPFD_CLOEXEC, 3);
if (lock_fd < 0) {
wlr_log(WLR_ERROR, "Failed to copy sleep lock fd: %s",
strerror(errno));
}
sd_bus_error_free(&error);
sd_bus_message_unref(msg);
}
static int prepare_for_sleep(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
/* "b" apparently reads into an int, not a bool */
int going_down = 1;
int ret = sd_bus_message_read(msg, "b", &going_down);
if (ret < 0) {
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for Inhibit: %s",
strerror(-ret));
}
wlr_log(WLR_DEBUG, "PrepareForSleep signal received %d", going_down);
if (!going_down) {
acquire_sleep_lock();
return 0;
}
ongoing_fd = lock_fd;
if (state.lock_cmd) {
cmd_exec(state.lock_cmd);
}
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(WLR_DEBUG, "Prepare for sleep done");
return 0;
}
static int dbus_event(int fd, uint32_t mask, void *data) {
sd_bus *bus = data;
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
sway_terminate(0);
}
int count = 0;
if (mask & WL_EVENT_READABLE) {
count = sd_bus_process(bus, NULL);
}
if (mask & WL_EVENT_WRITABLE) {
sd_bus_flush(bus);
}
if (mask == 0) {
sd_bus_flush(bus);
}
if (count < 0) {
wlr_log_errno(WLR_ERROR, "sd_bus_process failed, exiting");
sway_terminate(0);
}
return count;
}
static void setup_sleep_listener(void) {
int ret = sd_bus_default_system(&bus);
if (ret < 0) {
wlr_log(WLR_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(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
return;
}
acquire_sleep_lock();
struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
sd_bus_get_fd(bus), WL_EVENT_READABLE, dbus_event, bus);
wl_event_source_check(source);
}
#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) {
// Who cares
}
static const struct wl_registry_listener registry_listener = {
.global = handle_global,
.global_remove = handle_global_remove,
};
static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener;
static void register_timeout(struct swayidle_timeout_cmd *cmd,
int timeout) {
if (cmd->idle_timer != NULL) {
org_kde_kwin_idle_timeout_destroy(cmd->idle_timer);
cmd->idle_timer = NULL;
}
if (timeout < 0) {
wlr_log(WLR_DEBUG, "Not registering idle timeout");
return;
}
wlr_log(WLR_DEBUG, "Register with timeout: %d", timeout);
cmd->idle_timer =
org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout);
org_kde_kwin_idle_timeout_add_listener(cmd->idle_timer,
&idle_timer_listener, cmd);
cmd->registered_timeout = timeout;
}
static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) {
struct swayidle_timeout_cmd *cmd = data;
wlr_log(WLR_DEBUG, "idle state");
if (cmd->idle_cmd) {
cmd_exec(cmd->idle_cmd);
}
}
static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) {
struct swayidle_timeout_cmd *cmd = data;
wlr_log(WLR_DEBUG, "active state");
if (cmd->registered_timeout != cmd->timeout) {
register_timeout(cmd, cmd->timeout);
}
if (cmd->resume_cmd) {
cmd_exec(cmd->resume_cmd);
}
}
static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
.idle = handle_idle,
.resumed = handle_resume,
};
static char *parse_command(int argc, char **argv) {
if (argc < 1) {
wlr_log(WLR_ERROR, "Missing command");
return NULL;
}
wlr_log(WLR_DEBUG, "Command: %s", argv[0]);
return strdup(argv[0]);
}
static int parse_timeout(int argc, char **argv) {
if (argc < 3) {
wlr_log(WLR_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(WLR_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));
if (seconds > 0) {
cmd->timeout = seconds * 1000;
} else {
cmd->timeout = -1;
}
wlr_log(WLR_DEBUG, "Register idle timeout at %d ms", cmd->timeout);
wlr_log(WLR_DEBUG, "Setup idle");
cmd->idle_cmd = parse_command(argc - 2, &argv[2]);
int result = 3;
if (argc >= 5 && !strcmp("resume", argv[3])) {
wlr_log(WLR_DEBUG, "Setup resume");
cmd->resume_cmd = parse_command(argc - 4, &argv[4]);
result = 5;
}
list_add(state.timeout_cmds, cmd);
return result;
}
static int parse_sleep(int argc, char **argv) {
if (argc < 2) {
wlr_log(WLR_ERROR, "Too few parameters to before-sleep command. "
"Usage: before-sleep <command>");
exit(-1);
}
state.lock_cmd = parse_command(argc - 1, &argv[1]);
if (state.lock_cmd) {
wlr_log(WLR_DEBUG, "Setup sleep lock: %s", state.lock_cmd);
}
return 2;
}
static int parse_args(int argc, char *argv[]) {
bool debug = false;
int c;
while ((c = getopt(argc, argv, "hd")) != -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;
}
}
wlr_log_init(debug ? WLR_DEBUG : WLR_INFO, NULL);
state.timeout_cmds = create_list();
int i = optind;
while (i < argc) {
if (!strcmp("timeout", argv[i])) {
wlr_log(WLR_DEBUG, "Got timeout");
i += parse_timeout(argc - i, &argv[i]);
} else if (!strcmp("before-sleep", argv[i])) {
wlr_log(WLR_DEBUG, "Got before-sleep");
i += parse_sleep(argc - i, &argv[i]);
} else {
wlr_log(WLR_ERROR, "Unsupported command '%s'", argv[i]);
return 1;
}
}
return 0;
}
static int handle_signal(int sig, void *data) {
switch (sig) {
case SIGINT:
case SIGTERM:
sway_terminate(0);
return 0;
case SIGUSR1:
wlr_log(WLR_DEBUG, "Got SIGUSR1");
for (int i = 0; i < state.timeout_cmds->length; ++i) {
register_timeout(state.timeout_cmds->items[i], 0);
}
return 1;
}
assert(false); // not reached
}
static int display_event(int fd, uint32_t mask, void *data) {
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
sway_terminate(0);
}
int count = 0;
if (mask & WL_EVENT_READABLE) {
count = wl_display_dispatch(state.display);
}
if (mask & WL_EVENT_WRITABLE) {
wl_display_flush(state.display);
}
if (mask == 0) {
count = wl_display_dispatch_pending(state.display);
wl_display_flush(state.display);
}
if (count < 0) {
wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting");
sway_terminate(0);
}
return count;
}
int main(int argc, char *argv[]) {
if (parse_args(argc, argv) != 0) {
return -1;
}
state.event_loop = wl_event_loop_create();
wl_event_loop_add_signal(state.event_loop, SIGINT, handle_signal, NULL);
wl_event_loop_add_signal(state.event_loop, SIGTERM, handle_signal, NULL);
wl_event_loop_add_signal(state.event_loop, SIGUSR1, handle_signal, NULL);
state.display = wl_display_connect(NULL);
if (state.display == NULL) {
wlr_log(WLR_ERROR, "Unable to connect to the compositor. "
"If your compositor is running, check or set the "
"WAYLAND_DISPLAY environment variable.");
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);
if (idle_manager == NULL) {
wlr_log(WLR_ERROR, "Display doesn't support idle protocol");
return -4;
}
if (seat == NULL) {
wlr_log(WLR_ERROR, "Seat error");
return -5;
}
bool should_run = state.timeout_cmds->length > 0;
#if HAVE_SYSTEMD || HAVE_ELOGIND
if (state.lock_cmd) {
should_run = true;
setup_sleep_listener();
}
#endif
if (!should_run) {
wlr_log(WLR_INFO, "No command specified! Nothing to do, will exit");
sway_terminate(0);
}
for (int i = 0; i < state.timeout_cmds->length; ++i) {
struct swayidle_timeout_cmd *cmd = state.timeout_cmds->items[i];
register_timeout(cmd, cmd->timeout);
}
wl_display_roundtrip(state.display);
struct wl_event_source *source = wl_event_loop_add_fd(state.event_loop,
wl_display_get_fd(state.display), WL_EVENT_READABLE,
display_event, NULL);
wl_event_source_check(source);
while (wl_event_loop_dispatch(state.event_loop, -1) != 1) {
// This space intentionally left blank
}
sway_terminate(0);
}

View file

@ -1,27 +0,0 @@
threads = dependency('threads')
swayidle_deps = [
client_protos,
pixman,
wayland_client,
wayland_server,
wlroots,
]
if systemd.found()
swayidle_deps += systemd
endif
if elogind.found()
swayidle_deps += elogind
endif
executable(
'swayidle', [
'main.c',
],
include_directories: [sway_inc],
dependencies: swayidle_deps,
link_with: [lib_sway_common, lib_sway_client],
install_rpath : rpathdir,
install: true
)

View file

@ -1,63 +0,0 @@
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.
Sending SIGUSR1 to swayidle will immediately enter idle state.
# 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 300 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) *sway-input*(5) *sway-output*(5) *sway-bar*(5)