diff --git a/include/sway/commands.h b/include/sway/commands.h index 0732a90a4..d8b07a912 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -117,6 +117,7 @@ sway_cmd cmd_input; sway_cmd cmd_seat; sway_cmd cmd_ipc; sway_cmd cmd_kill; +sway_cmd cmd_lock; sway_cmd cmd_layout; sway_cmd cmd_log_colors; sway_cmd cmd_mark; @@ -148,6 +149,9 @@ sway_cmd cmd_unmark; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_workspace_layout; +sway_cmd cmd_idle_timeout; +sway_cmd cmd_lock_timeout; +sway_cmd cmd_swaylock_command; sway_cmd bar_cmd_activate_button; sway_cmd bar_cmd_binding_mode_indicator; diff --git a/include/sway/config.h b/include/sway/config.h index 87123289a..e4fdf64b8 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -305,6 +305,9 @@ struct sway_config { enum sway_container_layout default_layout; char *font; int font_height; + uint32_t idle_timeout; + uint32_t lock_timeout; + char *swaylock_command; // Flags bool focus_follows_mouse; diff --git a/include/sway/idle.h b/include/sway/idle.h new file mode 100644 index 000000000..e2a22e7e6 --- /dev/null +++ b/include/sway/idle.h @@ -0,0 +1,8 @@ +#ifndef _SWAY_IDLE_H +#define _SWAY_IDLE_H +#include + +void idle_setup_seat(struct sway_server *server, struct sway_seat *seat); +bool idle_init(struct sway_server *server); +void invoke_swaylock(); +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 296fbf224..ac685bf83 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -21,6 +21,7 @@ struct sway_server { struct wlr_compositor *compositor; struct wlr_data_device_manager *data_device_manager; + struct wlr_idle *idle; struct sway_input_manager *input; diff --git a/sway/commands.c b/sway/commands.c index a67bc1277..09ef05867 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -102,12 +102,15 @@ static struct cmd_handler handlers[] = { { "focus_follows_mouse", cmd_focus_follows_mouse }, { "for_window", cmd_for_window }, { "fullscreen", cmd_fullscreen }, + { "idle_timeout", cmd_idle_timeout }, { "include", cmd_include }, { "input", cmd_input }, + { "lock_timeout", cmd_lock_timeout }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, { "output", cmd_output }, { "seat", cmd_seat }, + { "swaylock_command", cmd_swaylock_command }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, }; @@ -166,6 +169,7 @@ static struct cmd_handler command_handlers[] = { { "focus", cmd_focus }, { "kill", cmd_kill }, { "layout", cmd_layout }, + { "lock", cmd_lock }, { "move", cmd_move }, { "opacity", cmd_opacity }, { "reload", cmd_reload }, @@ -423,7 +427,7 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) { struct cmd_handler *handler = find_handler(argv[0], block); if (!handler) { char *input = argv[0] ? argv[0] : "(empty)"; - results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); + results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command %d", block); goto cleanup; } int i; diff --git a/sway/commands/idle_timeout.c b/sway/commands/idle_timeout.c new file mode 100644 index 000000000..e5ae17c1c --- /dev/null +++ b/sway/commands/idle_timeout.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include "sway/commands.h" + +struct cmd_results *cmd_idle_timeout(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "idle_timeout", EXPECTED_EQUAL_TO, 1))) { + return error; + } + errno = 0; + config->idle_timeout = strtol(argv[0], NULL, 10); + if (errno == EINVAL || errno == ERANGE) + return cmd_results_new(CMD_INVALID, "idle_timeout", "Invalid timeout '%s'", argv[0]); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/lock.c b/sway/commands/lock.c new file mode 100644 index 000000000..b4495ad65 --- /dev/null +++ b/sway/commands/lock.c @@ -0,0 +1,11 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/idle.h" + +struct cmd_results *cmd_lock(int argc, char **argv) { + + invoke_swaylock(); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/lock_timeout.c b/sway/commands/lock_timeout.c new file mode 100644 index 000000000..b85157a22 --- /dev/null +++ b/sway/commands/lock_timeout.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include "sway/commands.h" + +struct cmd_results *cmd_lock_timeout(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "lock_timeout", EXPECTED_EQUAL_TO, 1))) { + return error; + } + errno = 0; + config->lock_timeout = strtol(argv[0], NULL, 10); + if (errno == EINVAL || errno == ERANGE) + return cmd_results_new(CMD_INVALID, "lock_timeout", "Invalid lock timeout."); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/swaylock_command.c b/sway/commands/swaylock_command.c new file mode 100644 index 000000000..27063cd2e --- /dev/null +++ b/sway/commands/swaylock_command.c @@ -0,0 +1,16 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *cmd_swaylock_command(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "swaylock_command", EXPECTED_AT_LEAST, 1))) { + return error; + } + free(config->swaylock_command); + config->swaylock_command = join_args(argv, argc); + wlr_log(L_DEBUG, "Using custom swaylock command: %s", + config->swaylock_command); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index 90b833ab7..952e11ef6 100644 --- a/sway/config.c +++ b/sway/config.c @@ -155,6 +155,9 @@ static void config_defaults(struct sway_config *config) { config->floating_mod = 0; config->dragging_key = BTN_LEFT; config->resizing_key = BTN_RIGHT; + config->idle_timeout = 600; + config->lock_timeout = 300; + config->swaylock_command = strdup("swaylock -c 000000"); if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; diff --git a/sway/idle.c b/sway/idle.c new file mode 100644 index 000000000..d999a8c76 --- /dev/null +++ b/sway/idle.c @@ -0,0 +1,159 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void invoke_swaylock() { + int pid = fork(); + if (pid == 0) { + char *const cmd[] = { "sh", "-c", config->swaylock_command, NULL, }; + execvp(cmd[0], cmd); + exit(1); + } + wlr_log(L_DEBUG, "Spawned swaylock %d", pid); +} + +bool have_lock(void *data) { + if (root_container.children == NULL) + return false; + if (root_container.children->items == NULL) + return false; + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *output_container = + root_container.children->items[i]; + if (output_container == NULL) + return false; + struct sway_output *output = + output_container->sway_output; + if (output == NULL) + return false; + if (output->layers == NULL) + return false; + struct wl_list *layers = &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]; + if (layers == NULL) + return false; + struct sway_layer_surface *sway_layer; + wl_list_for_each_reverse(sway_layer, layers, link) { + struct wlr_layer_surface *surface = sway_layer->layer_surface; + if (surface != NULL && surface->namespace != NULL) { + if (!strcmp("lockscreen", surface->namespace)) + wlr_log(L_DEBUG, "Lockscreen found!"); + return true; + } + } + } + + wlr_log(L_DEBUG, "No lockscreen found"); + return false; +} + +static void prepare_for_sleep(struct wlr_session *session, void *data) { + wlr_log(L_DEBUG, "PrepareForSleep signal received"); + invoke_swaylock(); +} + +void setup_sleep_listener(struct sway_server *server) { + struct wlr_session *session = NULL; + if (wlr_backend_is_multi(server->backend)) { + session = wlr_multi_get_session(server->backend); + } + if (!session) { + wlr_log(L_INFO, "No supported session found, skipping sleep litener setup"); + } + + wlr_session_prepare_for_sleep_listen(session, prepare_for_sleep, have_lock, server); +} + +static int handle_idle(void* data) { + wlr_log(L_DEBUG, "Idle state"); + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + if (cont->type != C_OUTPUT) { + continue; + } + if (cont->sway_output && cont->sway_output->wlr_output) { + wlr_output_enable(cont->sway_output->wlr_output, false); + } + } + return 0; +} + +static int handle_resume(void *data) { + wlr_log(L_DEBUG, "Active state"); + for (int i = 0; i < root_container.children->length; ++i) { + struct sway_container *cont = root_container.children->items[i]; + if (cont->type != C_OUTPUT) { + continue; + } + if (cont->sway_output && cont->sway_output->wlr_output) { + wlr_output_enable(cont->sway_output->wlr_output, true); + } + } + return 0; +} + +static const struct wlr_idle_timeout_listener idle_listener = { + .idle = handle_idle, + .resumed = handle_resume, +}; + +static int handle_lock(void* data) { + wlr_log(L_DEBUG, "Lock screen"); + invoke_swaylock(); + return 0; +} + +static int handle_nop(void *data) { + //NOP + return 0; +} + + +static const struct wlr_idle_timeout_listener lock_listener = { + .idle = handle_lock, + .resumed = handle_nop, +}; + +void idle_setup_seat(struct sway_server *server, struct sway_seat *seat) { + if (server->idle == NULL) { + return; + } + if (config != NULL) { + if (config->idle_timeout > 0) { + wlr_log(L_DEBUG, "Setup idle timer %d", config->idle_timeout); + wlr_idle_listen(server->idle, config->idle_timeout * 1000, &idle_listener, seat->wlr_seat); + } else { + wlr_log(L_INFO, "Idle timeout set to 0, will disable screen power management"); + } + if (config->lock_timeout > 0) { + wlr_log(L_DEBUG, "Setup lock timer %d", config->lock_timeout); + wlr_idle_listen(server->idle, config->lock_timeout * 1000, &lock_listener, seat->wlr_seat); + setup_sleep_listener(server); + } else { + wlr_log(L_INFO, "Lock timeout set to 0, will disable auto lock"); + } + } else { + wlr_log(L_ERROR, "Cant setup idle timers for seat since no config is available!"); + } +} + +bool idle_init(struct sway_server *server) { + wlr_log(L_DEBUG, "Initializing idle"); + server->idle = wlr_idle_create(server->wl_display); + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &input_manager->seats, link) { + idle_setup_seat(server, seat); + } + return true; +} + diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 944ad8eb9..ce0a7415f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -6,6 +6,7 @@ #endif #include #include +#include #include "list.h" #include "log.h" #include "sway/input/cursor.h" @@ -171,6 +172,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) { 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; wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); @@ -181,6 +183,7 @@ static void handle_cursor_motion_absolute( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = 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; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); cursor_send_pointer_motion(cursor, event->time_msec); @@ -230,6 +233,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, static void handle_cursor_button(struct wl_listener *listener, void *data) { 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; dispatch_cursor_button(cursor, event->time_msec, event->button, event->state); @@ -237,6 +241,7 @@ static void handle_cursor_button(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); + wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_axis *event = data; wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, event->orientation, event->delta); @@ -244,12 +249,14 @@ static void handle_cursor_axis(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); + wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_touch_down *event = data; wlr_log(L_DEBUG, "TODO: handle touch down event: %p", event); } static void handle_touch_up(struct wl_listener *listener, void *data) { 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; wlr_log(L_DEBUG, "TODO: handle touch up event: %p", event); } @@ -257,6 +264,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = 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; wlr_log(L_DEBUG, "TODO: handle touch motion event: %p", event); } @@ -297,6 +305,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device, static void handle_tool_axis(struct wl_listener *listener, void *data) { 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 sway_input_device *input_device = event->device->data; @@ -319,6 +328,7 @@ static void handle_tool_axis(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); + wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_tip *event = data; dispatch_cursor_button(cursor, event->time_msec, BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? @@ -327,6 +337,7 @@ static void handle_tool_tip(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); + wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_tablet_tool_button *event = data; // TODO: the user may want to configure which tool buttons are mapped to // which simulated pointer buttons diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index dbf2ce013..c07557db8 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "sway/input/seat.h" #include "sway/input/keyboard.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_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; xkb_keycode_t keycode = event->keycode + 8; diff --git a/sway/input/seat.c b/sway/input/seat.c index 963d38b2f..271a529ea 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -19,6 +19,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "sway/idle.h" #include "log.h" static void seat_device_destroy(struct sway_seat_device *seat_device) { @@ -257,6 +258,8 @@ struct sway_seat *seat_create(struct sway_input_manager *input, wl_list_insert(&input->seats, &seat->link); + idle_setup_seat(input->server, seat); + return seat; } diff --git a/sway/main.c b/sway/main.c index efb674b6b..d258faae1 100644 --- a/sway/main.c +++ b/sway/main.c @@ -18,6 +18,7 @@ #include #include "sway/config.h" #include "sway/debug.h" +#include "sway/idle.h" #include "sway/server.h" #include "sway/tree/layout.h" #include "sway/ipc-server.h" @@ -413,6 +414,7 @@ int main(int argc, char **argv) { // TODO: wait for server to be ready // TODO: consume config->cmd_queue config->active = true; + idle_init(&server); if (!terminate_request) { server_run(&server); diff --git a/sway/meson.build b/sway/meson.build index c9bb8f83d..cec4dfd0a 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -1,6 +1,7 @@ sway_sources = files( 'main.c', 'server.c', + 'idle.c', 'commands.c', 'config.c', 'criteria.c', @@ -38,6 +39,7 @@ sway_sources = files( 'commands/for_window.c', 'commands/fullscreen.c', 'commands/kill.c', + 'commands/lock.c', 'commands/opacity.c', 'commands/include.c', 'commands/input.c', @@ -58,6 +60,9 @@ sway_sources = files( 'commands/swaybg_command.c', 'commands/workspace.c', 'commands/ws_auto_back_and_forth.c', + 'commands/idle_timeout.c', + 'commands/lock_timeout.c', + 'commands/swaylock_command.c', 'commands/bar/activate_button.c', 'commands/bar/binding_mode_indicator.c',