Merge branch 'master' into fix-1975

This commit is contained in:
Brian Ashworth 2018-05-14 00:28:21 -04:00 committed by GitHub
commit 34b864fb17
Failed to generate hash of commit
22 changed files with 1074 additions and 13 deletions

View file

@ -8,6 +8,68 @@
#include <string.h>
#include "log.h"
int escape_markup_text(const char *src, char *dest, int dest_length) {
int length = 0;
while (src[0]) {
switch (src[0]) {
case '&':
length += 5;
if (dest && dest_length - length >= 0) {
dest += sprintf(dest, "%s", "&amp;");
} else {
dest_length = -1;
}
break;
case '<':
length += 4;
if (dest && dest_length - length >= 0) {
dest += sprintf(dest, "%s", "&lt;");
} else {
dest_length = -1;
}
break;
case '>':
length += 4;
if (dest && dest_length - length >= 0) {
dest += sprintf(dest, "%s", "&gt;");
} else {
dest_length = -1;
}
break;
case '\'':
length += 6;
if (dest && dest_length - length >= 0) {
dest += sprintf(dest, "%s", "&apos;");
} else {
dest_length = -1;
}
break;
case '"':
length += 6;
if (dest && dest_length - length >= 0) {
dest += sprintf(dest, "%s", "&quot;");
} else {
dest_length = -1;
}
break;
default:
length += 1;
if (dest && dest_length - length >= 0) {
*(dest++) = *src;
} else {
dest_length = -1;
}
}
src++;
}
// if we could not fit the escaped string in dest, return -1
if (dest && dest_length == -1) {
return -1;
}
return length;
}
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
const char *text, int32_t scale, bool markup) {
PangoLayout *layout = pango_cairo_create_layout(cairo);
@ -15,18 +77,21 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
if (markup) {
char *buf;
GError *error = NULL;
if (!sway_assert(pango_parse_markup(
text, -1, 0, &attrs, &buf, NULL, &error),
"pango_parse_markup '%s' -> error %s", text,
error ? error->message : NULL)) {
return NULL;
if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {
pango_layout_set_markup(layout, buf, -1);
free(buf);
} else {
wlr_log(L_ERROR, "pango_parse_markup '%s' -> error %s", text,
error->message);
g_error_free(error);
markup = false; // fallback to plain text
}
pango_layout_set_markup(layout, buf, -1);
free(buf);
} else {
}
if (!markup) {
attrs = pango_attr_list_new();
pango_layout_set_text(layout, text, -1);
}
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
PangoFontDescription *desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc);

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
### 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
#
# Example configuration:

View file

@ -6,6 +6,17 @@
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
/* Utility function which escape characters a & < > ' ".
*
* If the dest parameter is NULL, then the function returns the length of
* of the escaped src string. The dest_length doesn't matter.
*
* If the dest parameter is not NULL then the fuction escapes the src string
* an puts the escaped string in dest and returns the lenght of the escaped string.
* The dest_length parameter is the size of dest array. If the size of dest is not
* enough, then the function returns -1.
*/
int escape_markup_text(const char *src, char *dest, int dest_length);
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
const char *text, int32_t scale, bool markup);
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,

View file

@ -107,6 +107,12 @@ struct seat_config {
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.
*
@ -123,6 +129,7 @@ struct output_config {
char *background;
char *background_option;
enum config_dpms dpms_state;
};
/**

View file

@ -8,6 +8,7 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/render/wlr_renderer.h>
// TODO WLR: make Xwayland optional
#include <wlr/xwayland.h>
@ -21,6 +22,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;
@ -32,6 +34,9 @@ struct sway_server {
struct wlr_xdg_shell_v6 *xdg_shell_v6;
struct wl_listener xdg_shell_v6_surface;
struct wlr_xdg_shell *xdg_shell;
struct wl_listener xdg_shell_surface;
struct wlr_xwayland *xwayland;
struct wlr_xcursor_manager *xcursor_manager;
struct wl_listener xwayland_surface;
@ -51,6 +56,7 @@ void handle_new_output(struct wl_listener *listener, void *data);
void handle_layer_shell_surface(struct wl_listener *listener, void *data);
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
void handle_xwayland_surface(struct wl_listener *listener, void *data);
void handle_wl_shell_surface(struct wl_listener *listener, void *data);

View file

@ -12,6 +12,7 @@ struct sway_container;
enum sway_view_type {
SWAY_VIEW_WL_SHELL,
SWAY_VIEW_XDG_SHELL_V6,
SWAY_VIEW_XDG_SHELL,
SWAY_VIEW_XWAYLAND,
};
@ -58,6 +59,7 @@ struct sway_view {
union {
struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
struct wlr_xdg_surface *wlr_xdg_surface;
struct wlr_xwayland_surface *wlr_xwayland_surface;
struct wlr_wl_shell_surface *wlr_wl_shell_surface;
};
@ -86,6 +88,22 @@ struct sway_xdg_shell_v6_view {
int pending_width, pending_height;
};
struct sway_xdg_shell_view {
struct sway_view view;
struct wl_listener commit;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen;
struct wl_listener new_popup;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener destroy;
int pending_width, pending_height;
};
struct sway_xwayland_view {
struct sway_view view;
@ -160,6 +178,15 @@ struct sway_xdg_popup_v6 {
struct wl_listener destroy;
};
struct sway_xdg_popup {
struct sway_view_child child;
struct wlr_xdg_surface *wlr_xdg_surface;
struct wl_listener new_popup;
struct wl_listener destroy;
};
const char *view_get_title(struct sway_view *view);
const char *view_get_app_id(struct sway_view *view);

View file

@ -20,6 +20,8 @@ datadir = get_option('datadir')
sysconfdir = get_option('sysconfdir')
prefix = get_option('prefix')
swayidle_deps = []
jsonc = dependency('json-c', version: '>=0.13')
pcre = dependency('libpcre')
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
@ -37,6 +39,8 @@ pixman = dependency('pixman-1')
libcap = dependency('libcap')
libinput = dependency('libinput', version: '>=1.6.0')
libpam = cc.find_library('pam')
systemd = dependency('libsystemd', required: false)
elogind = dependency('libelogind', required: false)
math = cc.find_library('m')
rt = cc.find_library('rt')
git = find_program('git', required: false)
@ -47,6 +51,16 @@ if gdk_pixbuf.found()
conf_data.set('HAVE_GDK_PIXBUF', true)
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)
if scdoc.found()
@ -59,6 +73,7 @@ if scdoc.found()
'sway/sway-input.5.scd',
'swaylock/swaylock.1.scd',
'swaymsg/swaymsg.1.scd',
'swayidle/swayidle.1.scd',
]
foreach filename : man_files
topic = filename.split('.')[-3].split('/')[-1]
@ -106,6 +121,7 @@ subdir('client')
subdir('swaybg')
subdir('swaybar')
subdir('swaylock')
subdir('swayidle')
config = configuration_data()
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,10 +30,12 @@ wayland_scanner_server = generator(
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['idle.xml'],
['wlr-input-inhibitor-unstable-v1.xml']
]
server_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-input-inhibitor-unstable-v1.xml']

View file

@ -20,6 +20,25 @@ static char *bg_options[] = {
"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,
int *i, int argc, char **argv) {
if (++*i >= argc) {
@ -263,6 +282,8 @@ struct cmd_results *cmd_output(int argc, char **argv) {
} else if (strcasecmp(command, "background") == 0 ||
strcasecmp(command, "bg") == 0) {
error = cmd_output_background(output, &i, argc, argv);
} else if (strcasecmp(command, "dpms") == 0) {
error = cmd_output_dpms(output, &i, argc, argv);
} else {
error = cmd_results_new(CMD_INVALID, "output",
"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 "
"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->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
// 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->dragging_key = BTN_LEFT;
config->resizing_key = BTN_RIGHT;
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;

View file

@ -81,6 +81,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
free(dst->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,
@ -204,6 +207,20 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
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) {

277
sway/desktop/xdg_shell.c Normal file
View file

@ -0,0 +1,277 @@
#define _POSIX_C_SOURCE 199309L
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "sway/tree/container.h"
#include "sway/tree/layout.h"
#include "sway/server.h"
#include "sway/tree/view.h"
#include "sway/input/seat.h"
#include "sway/input/input-manager.h"
#include "log.h"
static const struct sway_view_child_impl popup_impl;
static void popup_destroy(struct sway_view_child *child) {
if (!sway_assert(child->impl == &popup_impl,
"Expected an xdg_shell popup")) {
return;
}
struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
wl_list_remove(&popup->new_popup.link);
wl_list_remove(&popup->destroy.link);
free(popup);
}
static const struct sway_view_child_impl popup_impl = {
.destroy = popup_destroy,
};
static struct sway_xdg_popup *popup_create(
struct wlr_xdg_popup *wlr_popup, struct sway_view *view);
static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_popup *popup =
wl_container_of(listener, popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
popup_create(wlr_popup, popup->child.view);
}
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
view_child_destroy(&popup->child);
}
static struct sway_xdg_popup *popup_create(
struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {
struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
struct sway_xdg_popup *popup =
calloc(1, sizeof(struct sway_xdg_popup));
if (popup == NULL) {
return NULL;
}
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
popup->destroy.notify = popup_handle_destroy;
return popup;
}
static struct sway_xdg_shell_view *xdg_shell_view_from_view(
struct sway_view *view) {
if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL,
"Expected xdg_shell view")) {
return NULL;
}
return (struct sway_xdg_shell_view *)view;
}
static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) {
if (xdg_shell_view_from_view(view) == NULL) {
return NULL;
}
switch (prop) {
case VIEW_PROP_TITLE:
return view->wlr_xdg_surface->toplevel->title;
case VIEW_PROP_APP_ID:
return view->wlr_xdg_surface->toplevel->app_id;
default:
return NULL;
}
}
static void configure(struct sway_view *view, double ox, double oy, int width,
int height) {
struct sway_xdg_shell_view *xdg_shell_view =
xdg_shell_view_from_view(view);
if (xdg_shell_view == NULL) {
return;
}
xdg_shell_view->pending_width = width;
xdg_shell_view->pending_height = height;
wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height);
}
static void set_activated(struct sway_view *view, bool activated) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wlr_xdg_toplevel_set_activated(surface, activated);
}
}
static void set_fullscreen(struct sway_view *view, bool fullscreen) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
}
static void for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator,
user_data);
}
static void _close(struct sway_view *view) {
if (xdg_shell_view_from_view(view) == NULL) {
return;
}
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wlr_xdg_surface_send_close(surface);
}
}
static void destroy(struct sway_view *view) {
struct sway_xdg_shell_view *xdg_shell_view =
xdg_shell_view_from_view(view);
if (xdg_shell_view == NULL) {
return;
}
wl_list_remove(&xdg_shell_view->destroy.link);
wl_list_remove(&xdg_shell_view->map.link);
wl_list_remove(&xdg_shell_view->unmap.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
free(xdg_shell_view);
}
static const struct sway_view_impl view_impl = {
.get_prop = get_prop,
.configure = configure,
.set_activated = set_activated,
.set_fullscreen = set_fullscreen,
.for_each_surface = for_each_surface,
.close = _close,
.destroy = destroy,
};
static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, commit);
struct sway_view *view = &xdg_shell_view->view;
// NOTE: We intentionally discard the view's desired width here
// TODO: Store this for restoration when moving to floating plane
// TODO: Let floating views do whatever
view_update_size(view, xdg_shell_view->pending_width,
xdg_shell_view->pending_height);
view_update_title(view, false);
view_damage_from(view);
}
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
popup_create(wlr_popup, &xdg_shell_view->view);
}
static void handle_unmap(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, unmap);
view_unmap(&xdg_shell_view->view);
wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->new_popup.link);
}
static void handle_map(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, map);
struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
view_map(view, view->wlr_xdg_surface->surface);
xdg_shell_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
&xdg_shell_view->commit);
xdg_shell_view->new_popup.notify = handle_new_popup;
wl_signal_add(&xdg_surface->events.new_popup,
&xdg_shell_view->new_popup);
if (xdg_surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true);
}
}
static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, destroy);
view_destroy(&xdg_shell_view->view);
}
static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_fullscreen);
struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
struct wlr_xdg_surface *xdg_surface =
xdg_shell_view->view.wlr_xdg_surface;
if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL,
"xdg_shell requested fullscreen of surface with role %i",
xdg_surface->role)) {
return;
}
if (!xdg_surface->mapped) {
return;
}
view_set_fullscreen(&xdg_shell_view->view, e->fullscreen);
}
void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
struct sway_server *server = wl_container_of(listener, server,
xdg_shell_surface);
struct wlr_xdg_surface *xdg_surface = data;
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
wlr_log(L_DEBUG, "New xdg_shell popup");
return;
}
wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
wlr_xdg_surface_ping(xdg_surface);
wlr_xdg_toplevel_set_maximized(xdg_surface, true);
struct sway_xdg_shell_view *xdg_shell_view =
calloc(1, sizeof(struct sway_xdg_shell_view));
if (!sway_assert(xdg_shell_view, "Failed to allocate view")) {
return;
}
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
// TODO:
// - Look up pid and open on appropriate workspace
xdg_shell_view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
xdg_shell_view->unmap.notify = handle_unmap;
wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
xdg_shell_view->destroy.notify = handle_destroy;
wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
&xdg_shell_view->request_fullscreen);
}

View file

@ -7,6 +7,7 @@
#endif
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_idle.h>
#include "list.h"
#include "log.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) {
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);
@ -182,6 +184,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);
@ -231,6 +234,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);
@ -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) {
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, 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) {
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_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) {
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_seat *seat = cursor->seat->wlr_seat;
// 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) {
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;
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) {
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;
@ -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) {
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 ?
@ -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) {
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

View file

@ -2,6 +2,7 @@
#include <limits.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_idle.h>
#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;

View file

@ -10,10 +10,11 @@ sway_sources = files(
'security.c',
'desktop/desktop.c',
'desktop/output.c',
'desktop/layer_shell.c',
'desktop/output.c',
'desktop/wl_shell.c',
'desktop/xdg_shell_v6.c',
'desktop/xdg_shell.c',
'desktop/xwayland.c',
'input/input-manager.c',

View file

@ -16,6 +16,7 @@
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_output.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/util/log.h>
// TODO WLR: make Xwayland optional
#include <wlr/xwayland.h>
@ -61,6 +62,7 @@ bool server_init(struct sway_server *server) {
server->data_device_manager =
wlr_data_device_manager_create(server->wl_display);
server->idle = wlr_idle_create(server->wl_display);
wlr_screenshooter_create(server->wl_display);
wlr_gamma_control_manager_create(server->wl_display);
wlr_primary_selection_device_manager_create(server->wl_display);
@ -81,6 +83,11 @@ bool server_init(struct sway_server *server) {
&server->xdg_shell_v6_surface);
server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface;
server->xdg_shell = wlr_xdg_shell_create(server->wl_display);
wl_signal_add(&server->xdg_shell->events.new_surface,
&server->xdg_shell_surface);
server->xdg_shell_surface.notify = handle_xdg_shell_surface;
server->wl_shell = wlr_wl_shell_create(server->wl_display);
wl_signal_add(&server->wl_shell->events.new_surface,
&server->wl_shell_surface);
@ -88,7 +95,7 @@ bool server_init(struct sway_server *server) {
// TODO make xwayland optional
server->xwayland =
wlr_xwayland_create(server->wl_display, server->compositor, false);
wlr_xwayland_create(server->wl_display, server->compositor, true);
wl_signal_add(&server->xwayland->events.new_surface,
&server->xwayland_surface);
server->xwayland_surface.notify = handle_xwayland_surface;

View file

@ -7,6 +7,8 @@
#include <wayland-server.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "cairo.h"
#include "pango.h"
#include "sway/config.h"
@ -459,6 +461,16 @@ struct sway_container *container_at(struct sway_container *parent,
sview->wlr_xdg_surface_v6,
view_sx, view_sy, &_sx, &_sy);
break;
case SWAY_VIEW_XDG_SHELL:
// the top left corner of the sway container is the
// coordinate of the top left corner of the window geometry
view_sx += sview->wlr_xdg_surface->geometry.x;
view_sy += sview->wlr_xdg_surface->geometry.y;
_surface = wlr_xdg_surface_surface_at(
sview->wlr_xdg_surface,
view_sx, view_sy, &_sx, &_sy);
break;
}
if (_surface) {
*sx = _sx;

View file

@ -14,6 +14,8 @@
#include "sway/tree/layout.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "sway/config.h"
#include "pango.h"
void view_init(struct sway_view *view, enum sway_view_type type,
const struct sway_view_impl *impl) {
@ -74,6 +76,8 @@ const char *view_get_type(struct sway_view *view) {
return "wl_shell";
case SWAY_VIEW_XDG_SHELL_V6:
return "xdg_shell_v6";
case SWAY_VIEW_XDG_SHELL:
return "xdg_shell";
case SWAY_VIEW_XWAYLAND:
return "xwayland";
}
@ -607,6 +611,19 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
return len;
}
static char *escape_title(char *buffer) {
int length = escape_markup_text(buffer, NULL, 0);
char *escaped_title = calloc(length + 1, sizeof(char));
int result = escape_markup_text(buffer, escaped_title, length);
if (result != length) {
wlr_log(L_ERROR, "Could not escape title: %s", buffer);
free(escaped_title);
return buffer;
}
free(buffer);
return escaped_title;
}
void view_update_title(struct sway_view *view, bool force) {
if (!view->swayc) {
return;
@ -626,11 +643,15 @@ void view_update_title(struct sway_view *view, bool force) {
free(view->swayc->formatted_title);
if (title) {
size_t len = parse_title_format(view, NULL);
char *buffer = calloc(len + 1, 1);
char *buffer = calloc(len + 1, sizeof(char));
if (!sway_assert(buffer, "Unable to allocate title string")) {
return;
}
parse_title_format(view, buffer);
// now we have the title, but needs to be escaped when using pango markup
if (config->pango_markup) {
buffer = escape_title(buffer);
}
view->swayc->name = strdup(title);
view->swayc->formatted_title = buffer;

415
swayidle/main.c Normal file
View file

@ -0,0 +1,415 @@
#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, -1) != 1) {
// This space intentionally left blank
}
sway_terminate(0);
}

18
swayidle/meson.build Normal file
View file

@ -0,0 +1,18 @@
threads = dependency('threads')
executable(
'swayidle', [
'main.c',
],
include_directories: [sway_inc],
dependencies: [
client_protos,
pixman,
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)