diff options
-rw-r--r-- | common/pango.c | 81 | ||||
-rw-r--r-- | config.in | 14 | ||||
-rw-r--r-- | include/pango.h | 11 | ||||
-rw-r--r-- | include/sway/config.h | 7 | ||||
-rw-r--r-- | include/sway/server.h | 6 | ||||
-rw-r--r-- | include/sway/tree/view.h | 27 | ||||
-rw-r--r-- | meson.build | 16 | ||||
-rw-r--r-- | protocols/idle.xml | 49 | ||||
-rw-r--r-- | protocols/meson.build | 2 | ||||
-rw-r--r-- | sway/commands/output.c | 25 | ||||
-rw-r--r-- | sway/config.c | 1 | ||||
-rw-r--r-- | sway/config/output.c | 17 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 277 | ||||
-rw-r--r-- | sway/input/cursor.c | 11 | ||||
-rw-r--r-- | sway/input/keyboard.c | 2 | ||||
-rw-r--r-- | sway/meson.build | 3 | ||||
-rw-r--r-- | sway/server.c | 9 | ||||
-rw-r--r-- | sway/tree/container.c | 12 | ||||
-rw-r--r-- | sway/tree/view.c | 23 | ||||
-rw-r--r-- | swayidle/main.c | 415 | ||||
-rw-r--r-- | swayidle/meson.build | 18 | ||||
-rw-r--r-- | swayidle/swayidle.1.scd | 61 |
22 files changed, 1074 insertions, 13 deletions
diff --git a/common/pango.c b/common/pango.c index 658d2876..403bd9a8 100644 --- a/common/pango.c +++ b/common/pango.c | |||
@@ -8,6 +8,68 @@ | |||
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include "log.h" | 9 | #include "log.h" |
10 | 10 | ||
11 | int escape_markup_text(const char *src, char *dest, int dest_length) { | ||
12 | int length = 0; | ||
13 | |||
14 | while (src[0]) { | ||
15 | switch (src[0]) { | ||
16 | case '&': | ||
17 | length += 5; | ||
18 | if (dest && dest_length - length >= 0) { | ||
19 | dest += sprintf(dest, "%s", "&"); | ||
20 | } else { | ||
21 | dest_length = -1; | ||
22 | } | ||
23 | break; | ||
24 | case '<': | ||
25 | length += 4; | ||
26 | if (dest && dest_length - length >= 0) { | ||
27 | dest += sprintf(dest, "%s", "<"); | ||
28 | } else { | ||
29 | dest_length = -1; | ||
30 | } | ||
31 | break; | ||
32 | case '>': | ||
33 | length += 4; | ||
34 | if (dest && dest_length - length >= 0) { | ||
35 | dest += sprintf(dest, "%s", ">"); | ||
36 | } else { | ||
37 | dest_length = -1; | ||
38 | } | ||
39 | break; | ||
40 | case '\'': | ||
41 | length += 6; | ||
42 | if (dest && dest_length - length >= 0) { | ||
43 | dest += sprintf(dest, "%s", "'"); | ||
44 | } else { | ||
45 | dest_length = -1; | ||
46 | } | ||
47 | break; | ||
48 | case '"': | ||
49 | length += 6; | ||
50 | if (dest && dest_length - length >= 0) { | ||
51 | dest += sprintf(dest, "%s", """); | ||
52 | } else { | ||
53 | dest_length = -1; | ||
54 | } | ||
55 | break; | ||
56 | default: | ||
57 | length += 1; | ||
58 | if (dest && dest_length - length >= 0) { | ||
59 | *(dest++) = *src; | ||
60 | } else { | ||
61 | dest_length = -1; | ||
62 | } | ||
63 | } | ||
64 | src++; | ||
65 | } | ||
66 | // if we could not fit the escaped string in dest, return -1 | ||
67 | if (dest && dest_length == -1) { | ||
68 | return -1; | ||
69 | } | ||
70 | return length; | ||
71 | } | ||
72 | |||
11 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 73 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, |
12 | const char *text, int32_t scale, bool markup) { | 74 | const char *text, int32_t scale, bool markup) { |
13 | PangoLayout *layout = pango_cairo_create_layout(cairo); | 75 | PangoLayout *layout = pango_cairo_create_layout(cairo); |
@@ -15,18 +77,21 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | |||
15 | if (markup) { | 77 | if (markup) { |
16 | char *buf; | 78 | char *buf; |
17 | GError *error = NULL; | 79 | GError *error = NULL; |
18 | if (!sway_assert(pango_parse_markup( | 80 | if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { |
19 | text, -1, 0, &attrs, &buf, NULL, &error), | 81 | pango_layout_set_markup(layout, buf, -1); |
20 | "pango_parse_markup '%s' -> error %s", text, | 82 | free(buf); |
21 | error ? error->message : NULL)) { | 83 | } else { |
22 | return NULL; | 84 | wlr_log(L_ERROR, "pango_parse_markup '%s' -> error %s", text, |
85 | error->message); | ||
86 | g_error_free(error); | ||
87 | markup = false; // fallback to plain text | ||
23 | } | 88 | } |
24 | pango_layout_set_markup(layout, buf, -1); | 89 | } |
25 | free(buf); | 90 | if (!markup) { |
26 | } else { | ||
27 | attrs = pango_attr_list_new(); | 91 | attrs = pango_attr_list_new(); |
28 | pango_layout_set_text(layout, text, -1); | 92 | pango_layout_set_text(layout, text, -1); |
29 | } | 93 | } |
94 | |||
30 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); | 95 | pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); |
31 | PangoFontDescription *desc = pango_font_description_from_string(font); | 96 | PangoFontDescription *desc = pango_font_description_from_string(font); |
32 | pango_layout_set_font_description(layout, desc); | 97 | pango_layout_set_font_description(layout, desc); |
@@ -29,6 +29,20 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill | |||
29 | # | 29 | # |
30 | # You can get the names of your outputs by running: swaymsg -t get_outputs | 30 | # You can get the names of your outputs by running: swaymsg -t get_outputs |
31 | 31 | ||
32 | ### Idle configuration | ||
33 | # | ||
34 | # Example configuration: | ||
35 | # | ||
36 | #exec swayidle \ | ||
37 | # timeout 300 'swaylock -c 000000' \ | ||
38 | # timeout 600 'swaymsg "output * dpms off"' \ | ||
39 | # resume 'swaymsg "output * dpms on"' \ | ||
40 | # before-sleep 'swaylock -c 000000' | ||
41 | # | ||
42 | # This will lock your screen after 300 seconds of inactivity, then turn off | ||
43 | # your displays after another 600 seconds, and turn your screens back on when | ||
44 | # resumed. It will also lock your screen before your computer goes to sleep. | ||
45 | |||
32 | ### Input configuration | 46 | ### Input configuration |
33 | # | 47 | # |
34 | # Example configuration: | 48 | # Example configuration: |
diff --git a/include/pango.h b/include/pango.h index f6325f28..d8263f9e 100644 --- a/include/pango.h +++ b/include/pango.h | |||
@@ -6,6 +6,17 @@ | |||
6 | #include <cairo/cairo.h> | 6 | #include <cairo/cairo.h> |
7 | #include <pango/pangocairo.h> | 7 | #include <pango/pangocairo.h> |
8 | 8 | ||
9 | /* Utility function which escape characters a & < > ' ". | ||
10 | * | ||
11 | * If the dest parameter is NULL, then the function returns the length of | ||
12 | * of the escaped src string. The dest_length doesn't matter. | ||
13 | * | ||
14 | * If the dest parameter is not NULL then the fuction escapes the src string | ||
15 | * an puts the escaped string in dest and returns the lenght of the escaped string. | ||
16 | * The dest_length parameter is the size of dest array. If the size of dest is not | ||
17 | * enough, then the function returns -1. | ||
18 | */ | ||
19 | int escape_markup_text(const char *src, char *dest, int dest_length); | ||
9 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, | 20 | PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, |
10 | const char *text, int32_t scale, bool markup); | 21 | const char *text, int32_t scale, bool markup); |
11 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, | 22 | void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, |
diff --git a/include/sway/config.h b/include/sway/config.h index b20458cb..f77c3b50 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -107,6 +107,12 @@ struct seat_config { | |||
107 | list_t *attachments; // list of seat_attachment configs | 107 | list_t *attachments; // list of seat_attachment configs |
108 | }; | 108 | }; |
109 | 109 | ||
110 | enum config_dpms { | ||
111 | DPMS_IGNORE, | ||
112 | DPMS_ON, | ||
113 | DPMS_OFF | ||
114 | }; | ||
115 | |||
110 | /** | 116 | /** |
111 | * Size and position configuration for a particular output. | 117 | * Size and position configuration for a particular output. |
112 | * | 118 | * |
@@ -123,6 +129,7 @@ struct output_config { | |||
123 | 129 | ||
124 | char *background; | 130 | char *background; |
125 | char *background_option; | 131 | char *background_option; |
132 | enum config_dpms dpms_state; | ||
126 | }; | 133 | }; |
127 | 134 | ||
128 | /** | 135 | /** |
diff --git a/include/sway/server.h b/include/sway/server.h index 296fbf22..d04ea896 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <wlr/types/wlr_data_device.h> | 8 | #include <wlr/types/wlr_data_device.h> |
9 | #include <wlr/types/wlr_layer_shell.h> | 9 | #include <wlr/types/wlr_layer_shell.h> |
10 | #include <wlr/types/wlr_xdg_shell_v6.h> | 10 | #include <wlr/types/wlr_xdg_shell_v6.h> |
11 | #include <wlr/types/wlr_xdg_shell.h> | ||
11 | #include <wlr/render/wlr_renderer.h> | 12 | #include <wlr/render/wlr_renderer.h> |
12 | // TODO WLR: make Xwayland optional | 13 | // TODO WLR: make Xwayland optional |
13 | #include <wlr/xwayland.h> | 14 | #include <wlr/xwayland.h> |
@@ -21,6 +22,7 @@ struct sway_server { | |||
21 | 22 | ||
22 | struct wlr_compositor *compositor; | 23 | struct wlr_compositor *compositor; |
23 | struct wlr_data_device_manager *data_device_manager; | 24 | struct wlr_data_device_manager *data_device_manager; |
25 | struct wlr_idle *idle; | ||
24 | 26 | ||
25 | struct sway_input_manager *input; | 27 | struct sway_input_manager *input; |
26 | 28 | ||
@@ -32,6 +34,9 @@ struct sway_server { | |||
32 | struct wlr_xdg_shell_v6 *xdg_shell_v6; | 34 | struct wlr_xdg_shell_v6 *xdg_shell_v6; |
33 | struct wl_listener xdg_shell_v6_surface; | 35 | struct wl_listener xdg_shell_v6_surface; |
34 | 36 | ||
37 | struct wlr_xdg_shell *xdg_shell; | ||
38 | struct wl_listener xdg_shell_surface; | ||
39 | |||
35 | struct wlr_xwayland *xwayland; | 40 | struct wlr_xwayland *xwayland; |
36 | struct wlr_xcursor_manager *xcursor_manager; | 41 | struct wlr_xcursor_manager *xcursor_manager; |
37 | struct wl_listener xwayland_surface; | 42 | struct wl_listener xwayland_surface; |
@@ -51,6 +56,7 @@ void handle_new_output(struct wl_listener *listener, void *data); | |||
51 | 56 | ||
52 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 57 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
53 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); | 58 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); |
59 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); | ||
54 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 60 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
55 | void handle_wl_shell_surface(struct wl_listener *listener, void *data); | 61 | void handle_wl_shell_surface(struct wl_listener *listener, void *data); |
56 | 62 | ||
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index de431c89..e163e3fe 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -12,6 +12,7 @@ struct sway_container; | |||
12 | enum sway_view_type { | 12 | enum sway_view_type { |
13 | SWAY_VIEW_WL_SHELL, | 13 | SWAY_VIEW_WL_SHELL, |
14 | SWAY_VIEW_XDG_SHELL_V6, | 14 | SWAY_VIEW_XDG_SHELL_V6, |
15 | SWAY_VIEW_XDG_SHELL, | ||
15 | SWAY_VIEW_XWAYLAND, | 16 | SWAY_VIEW_XWAYLAND, |
16 | }; | 17 | }; |
17 | 18 | ||
@@ -58,6 +59,7 @@ struct sway_view { | |||
58 | 59 | ||
59 | union { | 60 | union { |
60 | struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; | 61 | struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; |
62 | struct wlr_xdg_surface *wlr_xdg_surface; | ||
61 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 63 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
62 | struct wlr_wl_shell_surface *wlr_wl_shell_surface; | 64 | struct wlr_wl_shell_surface *wlr_wl_shell_surface; |
63 | }; | 65 | }; |
@@ -86,6 +88,22 @@ struct sway_xdg_shell_v6_view { | |||
86 | int pending_width, pending_height; | 88 | int pending_width, pending_height; |
87 | }; | 89 | }; |
88 | 90 | ||
91 | struct sway_xdg_shell_view { | ||
92 | struct sway_view view; | ||
93 | |||
94 | struct wl_listener commit; | ||
95 | struct wl_listener request_move; | ||
96 | struct wl_listener request_resize; | ||
97 | struct wl_listener request_maximize; | ||
98 | struct wl_listener request_fullscreen; | ||
99 | struct wl_listener new_popup; | ||
100 | struct wl_listener map; | ||
101 | struct wl_listener unmap; | ||
102 | struct wl_listener destroy; | ||
103 | |||
104 | int pending_width, pending_height; | ||
105 | }; | ||
106 | |||
89 | struct sway_xwayland_view { | 107 | struct sway_xwayland_view { |
90 | struct sway_view view; | 108 | struct sway_view view; |
91 | 109 | ||
@@ -160,6 +178,15 @@ struct sway_xdg_popup_v6 { | |||
160 | struct wl_listener destroy; | 178 | struct wl_listener destroy; |
161 | }; | 179 | }; |
162 | 180 | ||
181 | struct sway_xdg_popup { | ||
182 | struct sway_view_child child; | ||
183 | |||
184 | struct wlr_xdg_surface *wlr_xdg_surface; | ||
185 | |||
186 | struct wl_listener new_popup; | ||
187 | struct wl_listener destroy; | ||
188 | }; | ||
189 | |||
163 | const char *view_get_title(struct sway_view *view); | 190 | const char *view_get_title(struct sway_view *view); |
164 | 191 | ||
165 | const char *view_get_app_id(struct sway_view *view); | 192 | const char *view_get_app_id(struct sway_view *view); |
diff --git a/meson.build b/meson.build index a1f406ec..b943236f 100644 --- a/meson.build +++ b/meson.build | |||
@@ -20,6 +20,8 @@ datadir = get_option('datadir') | |||
20 | sysconfdir = get_option('sysconfdir') | 20 | sysconfdir = get_option('sysconfdir') |
21 | prefix = get_option('prefix') | 21 | prefix = get_option('prefix') |
22 | 22 | ||
23 | swayidle_deps = [] | ||
24 | |||
23 | jsonc = dependency('json-c', version: '>=0.13') | 25 | jsonc = dependency('json-c', version: '>=0.13') |
24 | pcre = dependency('libpcre') | 26 | pcre = dependency('libpcre') |
25 | wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) | 27 | wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) |
@@ -37,6 +39,8 @@ pixman = dependency('pixman-1') | |||
37 | libcap = dependency('libcap') | 39 | libcap = dependency('libcap') |
38 | libinput = dependency('libinput', version: '>=1.6.0') | 40 | libinput = dependency('libinput', version: '>=1.6.0') |
39 | libpam = cc.find_library('pam') | 41 | libpam = cc.find_library('pam') |
42 | systemd = dependency('libsystemd', required: false) | ||
43 | elogind = dependency('libelogind', required: false) | ||
40 | math = cc.find_library('m') | 44 | math = cc.find_library('m') |
41 | rt = cc.find_library('rt') | 45 | rt = cc.find_library('rt') |
42 | git = find_program('git', required: false) | 46 | git = find_program('git', required: false) |
@@ -47,6 +51,16 @@ if gdk_pixbuf.found() | |||
47 | conf_data.set('HAVE_GDK_PIXBUF', true) | 51 | conf_data.set('HAVE_GDK_PIXBUF', true) |
48 | endif | 52 | endif |
49 | 53 | ||
54 | if systemd.found() | ||
55 | conf_data.set('SWAY_IDLE_HAS_SYSTEMD', true) | ||
56 | swayidle_deps += systemd | ||
57 | endif | ||
58 | |||
59 | if elogind.found() | ||
60 | conf_data.set('SWAY_IDLE_HAS_ELOGIND', true) | ||
61 | swayidle_deps += elogind | ||
62 | endif | ||
63 | |||
50 | scdoc = find_program('scdoc', required: false) | 64 | scdoc = find_program('scdoc', required: false) |
51 | 65 | ||
52 | if scdoc.found() | 66 | if scdoc.found() |
@@ -59,6 +73,7 @@ if scdoc.found() | |||
59 | 'sway/sway-input.5.scd', | 73 | 'sway/sway-input.5.scd', |
60 | 'swaylock/swaylock.1.scd', | 74 | 'swaylock/swaylock.1.scd', |
61 | 'swaymsg/swaymsg.1.scd', | 75 | 'swaymsg/swaymsg.1.scd', |
76 | 'swayidle/swayidle.1.scd', | ||
62 | ] | 77 | ] |
63 | foreach filename : man_files | 78 | foreach filename : man_files |
64 | topic = filename.split('.')[-3].split('/')[-1] | 79 | topic = filename.split('.')[-3].split('/')[-1] |
@@ -106,6 +121,7 @@ subdir('client') | |||
106 | subdir('swaybg') | 121 | subdir('swaybg') |
107 | subdir('swaybar') | 122 | subdir('swaybar') |
108 | subdir('swaylock') | 123 | subdir('swaylock') |
124 | subdir('swayidle') | ||
109 | 125 | ||
110 | config = configuration_data() | 126 | config = configuration_data() |
111 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 127 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) |
diff --git a/protocols/idle.xml b/protocols/idle.xml new file mode 100644 index 00000000..92d9989c --- /dev/null +++ b/protocols/idle.xml | |||
@@ -0,0 +1,49 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <protocol name="idle"> | ||
3 | <copyright><![CDATA[ | ||
4 | Copyright (C) 2015 Martin Gräßlin | ||
5 | |||
6 | This program is free software: you can redistribute it and/or modify | ||
7 | it under the terms of the GNU Lesser General Public License as published by | ||
8 | the Free Software Foundation, either version 2.1 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public License | ||
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | ]]></copyright> | ||
19 | <interface name="org_kde_kwin_idle" version="1"> | ||
20 | <description summary="User idle time manager"> | ||
21 | This interface allows to monitor user idle time on a given seat. The interface | ||
22 | allows to register timers which trigger after no user activity was registered | ||
23 | on the seat for a given interval. It notifies when user activity resumes. | ||
24 | |||
25 | This is useful for applications wanting to perform actions when the user is not | ||
26 | interacting with the system, e.g. chat applications setting the user as away, power | ||
27 | management features to dim screen, etc.. | ||
28 | </description> | ||
29 | <request name="get_idle_timeout"> | ||
30 | <arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/> | ||
31 | <arg name="seat" type="object" interface="wl_seat"/> | ||
32 | <arg name="timeout" type="uint" summary="The idle timeout in msec"/> | ||
33 | </request> | ||
34 | </interface> | ||
35 | <interface name="org_kde_kwin_idle_timeout" version="1"> | ||
36 | <request name="release" type="destructor"> | ||
37 | <description summary="release the timeout object"/> | ||
38 | </request> | ||
39 | <request name="simulate_user_activity"> | ||
40 | <description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/> | ||
41 | </request> | ||
42 | <event name="idle"> | ||
43 | <description summary="Triggered when there has not been any user activity in the requested idle time interval"/> | ||
44 | </event> | ||
45 | <event name="resumed"> | ||
46 | <description summary="Triggered on the first user activity after an idle event"/> | ||
47 | </event> | ||
48 | </interface> | ||
49 | </protocol> | ||
diff --git a/protocols/meson.build b/protocols/meson.build index 9d213f81..9966c02f 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -30,10 +30,12 @@ wayland_scanner_server = generator( | |||
30 | client_protocols = [ | 30 | client_protocols = [ |
31 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | 31 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], |
32 | ['wlr-layer-shell-unstable-v1.xml'], | 32 | ['wlr-layer-shell-unstable-v1.xml'], |
33 | ['idle.xml'], | ||
33 | ['wlr-input-inhibitor-unstable-v1.xml'] | 34 | ['wlr-input-inhibitor-unstable-v1.xml'] |
34 | ] | 35 | ] |
35 | 36 | ||
36 | server_protocols = [ | 37 | server_protocols = [ |
38 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | ||
37 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], | 39 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], |
38 | ['wlr-layer-shell-unstable-v1.xml'], | 40 | ['wlr-layer-shell-unstable-v1.xml'], |
39 | ['wlr-input-inhibitor-unstable-v1.xml'] | 41 | ['wlr-input-inhibitor-unstable-v1.xml'] |
diff --git a/sway/commands/output.c b/sway/commands/output.c index f7e3372c..e8881f77 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c | |||
@@ -20,6 +20,25 @@ static char *bg_options[] = { | |||
20 | "tile", | 20 | "tile", |
21 | }; | 21 | }; |
22 | 22 | ||
23 | static struct cmd_results *cmd_output_dpms(struct output_config *output, | ||
24 | int *i, int argc, char **argv) { | ||
25 | |||
26 | if (++*i >= argc) { | ||
27 | return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); | ||
28 | } | ||
29 | |||
30 | char *value = argv[*i]; | ||
31 | if (strcmp(value, "on") == 0) { | ||
32 | output->dpms_state = DPMS_ON; | ||
33 | } else if (strcmp(value, "off") == 0) { | ||
34 | output->dpms_state = DPMS_OFF; | ||
35 | } else { | ||
36 | return cmd_results_new(CMD_INVALID, "output", | ||
37 | "Invalid dpms state, valid states are on/off."); | ||
38 | } | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
23 | static struct cmd_results *cmd_output_mode(struct output_config *output, | 42 | static struct cmd_results *cmd_output_mode(struct output_config *output, |
24 | int *i, int argc, char **argv) { | 43 | int *i, int argc, char **argv) { |
25 | if (++*i >= argc) { | 44 | if (++*i >= argc) { |
@@ -263,6 +282,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
263 | } else if (strcasecmp(command, "background") == 0 || | 282 | } else if (strcasecmp(command, "background") == 0 || |
264 | strcasecmp(command, "bg") == 0) { | 283 | strcasecmp(command, "bg") == 0) { |
265 | error = cmd_output_background(output, &i, argc, argv); | 284 | error = cmd_output_background(output, &i, argc, argv); |
285 | } else if (strcasecmp(command, "dpms") == 0) { | ||
286 | error = cmd_output_dpms(output, &i, argc, argv); | ||
266 | } else { | 287 | } else { |
267 | error = cmd_results_new(CMD_INVALID, "output", | 288 | error = cmd_results_new(CMD_INVALID, "output", |
268 | "Invalid output subcommand: %s.", command); | 289 | "Invalid output subcommand: %s.", command); |
@@ -285,10 +306,10 @@ struct cmd_results *cmd_output(int argc, char **argv) { | |||
285 | } | 306 | } |
286 | 307 | ||
287 | wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 308 | wlr_log(L_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
288 | "position %d,%d scale %f transform %d) (bg %s %s)", | 309 | "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", |
289 | output->name, output->enabled, output->width, output->height, | 310 | output->name, output->enabled, output->width, output->height, |
290 | output->refresh_rate, output->x, output->y, output->scale, | 311 | output->refresh_rate, output->x, output->y, output->scale, |
291 | output->transform, output->background, output->background_option); | 312 | output->transform, output->background, output->background_option, output->dpms_state); |
292 | 313 | ||
293 | // Try to find the output container and apply configuration now. If | 314 | // Try to find the output container and apply configuration now. If |
294 | // this is during startup then there will be no container and config | 315 | // this is during startup then there will be no container and config |
diff --git a/sway/config.c b/sway/config.c index 270c1d86..34c8a280 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -165,6 +165,7 @@ static void config_defaults(struct sway_config *config) { | |||
165 | config->floating_mod = 0; | 165 | config->floating_mod = 0; |
166 | config->dragging_key = BTN_LEFT; | 166 | config->dragging_key = BTN_LEFT; |
167 | config->resizing_key = BTN_RIGHT; | 167 | config->resizing_key = BTN_RIGHT; |
168 | |||
168 | if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; | 169 | if (!(config->floating_scroll_up_cmd = strdup(""))) goto cleanup; |
169 | if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; | 170 | if (!(config->floating_scroll_down_cmd = strdup(""))) goto cleanup; |
170 | if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; | 171 | if (!(config->floating_scroll_left_cmd = strdup(""))) goto cleanup; |
diff --git a/sway/config/output.c b/sway/config/output.c index 68022278..ee2440ea 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -81,6 +81,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { | |||
81 | free(dst->background_option); | 81 | free(dst->background_option); |
82 | dst->background_option = strdup(src->background_option); | 82 | dst->background_option = strdup(src->background_option); |
83 | } | 83 | } |
84 | if (src->dpms_state != 0) { | ||
85 | dst->dpms_state = src->dpms_state; | ||
86 | } | ||
84 | } | 87 | } |
85 | 88 | ||
86 | static void set_mode(struct wlr_output *output, int width, int height, | 89 | 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 | |||
204 | execvp(cmd[0], cmd); | 207 | execvp(cmd[0], cmd); |
205 | } | 208 | } |
206 | } | 209 | } |
210 | if (oc && oc->dpms_state != DPMS_IGNORE) { | ||
211 | switch (oc->dpms_state) { | ||
212 | case DPMS_ON: | ||
213 | wlr_log(L_DEBUG, "Turning on screen"); | ||
214 | wlr_output_enable(wlr_output, true); | ||
215 | break; | ||
216 | case DPMS_OFF: | ||
217 | wlr_log(L_DEBUG, "Turning off screen"); | ||
218 | wlr_output_enable(wlr_output, false); | ||
219 | break; | ||
220 | case DPMS_IGNORE: | ||
221 | break; | ||
222 | } | ||
223 | } | ||
207 | } | 224 | } |
208 | 225 | ||
209 | void free_output_config(struct output_config *oc) { | 226 | void free_output_config(struct output_config *oc) { |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c new file mode 100644 index 00000000..48b659f2 --- /dev/null +++ b/sway/desktop/xdg_shell.c | |||
@@ -0,0 +1,277 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <wayland-server.h> | ||
5 | #include <wlr/types/wlr_xdg_shell.h> | ||
6 | #include "sway/tree/container.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | #include "sway/server.h" | ||
9 | #include "sway/tree/view.h" | ||
10 | #include "sway/input/seat.h" | ||
11 | #include "sway/input/input-manager.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | static const struct sway_view_child_impl popup_impl; | ||
15 | |||
16 | static void popup_destroy(struct sway_view_child *child) { | ||
17 | if (!sway_assert(child->impl == &popup_impl, | ||
18 | "Expected an xdg_shell popup")) { | ||
19 | return; | ||
20 | } | ||
21 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
22 | wl_list_remove(&popup->new_popup.link); | ||
23 | wl_list_remove(&popup->destroy.link); | ||
24 | free(popup); | ||
25 | } | ||
26 | |||
27 | static const struct sway_view_child_impl popup_impl = { | ||
28 | .destroy = popup_destroy, | ||
29 | }; | ||
30 | |||
31 | static struct sway_xdg_popup *popup_create( | ||
32 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | ||
33 | |||
34 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | ||
35 | struct sway_xdg_popup *popup = | ||
36 | wl_container_of(listener, popup, new_popup); | ||
37 | struct wlr_xdg_popup *wlr_popup = data; | ||
38 | popup_create(wlr_popup, popup->child.view); | ||
39 | } | ||
40 | |||
41 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | ||
42 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | ||
43 | view_child_destroy(&popup->child); | ||
44 | } | ||
45 | |||
46 | static struct sway_xdg_popup *popup_create( | ||
47 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | ||
48 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | ||
49 | |||
50 | struct sway_xdg_popup *popup = | ||
51 | calloc(1, sizeof(struct sway_xdg_popup)); | ||
52 | if (popup == NULL) { | ||
53 | return NULL; | ||
54 | } | ||
55 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
56 | |||
57 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | ||
58 | popup->new_popup.notify = popup_handle_new_popup; | ||
59 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | ||
60 | popup->destroy.notify = popup_handle_destroy; | ||
61 | |||
62 | return popup; | ||
63 | } | ||
64 | |||
65 | |||
66 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | ||
67 | struct sway_view *view) { | ||
68 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | ||
69 | "Expected xdg_shell view")) { | ||
70 | return NULL; | ||
71 | } | ||
72 | return (struct sway_xdg_shell_view *)view; | ||
73 | } | ||
74 | |||
75 | static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { | ||
76 | if (xdg_shell_view_from_view(view) == NULL) { | ||
77 | return NULL; | ||
78 | } | ||
79 | switch (prop) { | ||
80 | case VIEW_PROP_TITLE: | ||
81 | return view->wlr_xdg_surface->toplevel->title; | ||
82 | case VIEW_PROP_APP_ID: | ||
83 | return view->wlr_xdg_surface->toplevel->app_id; | ||
84 | default: | ||
85 | return NULL; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void configure(struct sway_view *view, double ox, double oy, int width, | ||
90 | int height) { | ||
91 | struct sway_xdg_shell_view *xdg_shell_view = | ||
92 | xdg_shell_view_from_view(view); | ||
93 | if (xdg_shell_view == NULL) { | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | xdg_shell_view->pending_width = width; | ||
98 | xdg_shell_view->pending_height = height; | ||
99 | wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | ||
100 | } | ||
101 | |||
102 | static void set_activated(struct sway_view *view, bool activated) { | ||
103 | if (xdg_shell_view_from_view(view) == NULL) { | ||
104 | return; | ||
105 | } | ||
106 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | ||
107 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
108 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | ||
113 | if (xdg_shell_view_from_view(view) == NULL) { | ||
114 | return; | ||
115 | } | ||
116 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | ||
117 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
118 | } | ||
119 | |||
120 | static void for_each_surface(struct sway_view *view, | ||
121 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
122 | if (xdg_shell_view_from_view(view) == NULL) { | ||
123 | return; | ||
124 | } | ||
125 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
126 | user_data); | ||
127 | } | ||
128 | |||
129 | static void _close(struct sway_view *view) { | ||
130 | if (xdg_shell_view_from_view(view) == NULL) { | ||
131 | return; | ||
132 | } | ||
133 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | ||
134 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
135 | wlr_xdg_surface_send_close(surface); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | static void destroy(struct sway_view *view) { | ||
140 | struct sway_xdg_shell_view *xdg_shell_view = | ||
141 | xdg_shell_view_from_view(view); | ||
142 | if (xdg_shell_view == NULL) { | ||
143 | return; | ||
144 | } | ||
145 | wl_list_remove(&xdg_shell_view->destroy.link); | ||
146 | wl_list_remove(&xdg_shell_view->map.link); | ||
147 | wl_list_remove(&xdg_shell_view->unmap.link); | ||
148 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
149 | free(xdg_shell_view); | ||
150 | } | ||
151 | |||
152 | static const struct sway_view_impl view_impl = { | ||
153 | .get_prop = get_prop, | ||
154 | .configure = configure, | ||
155 | .set_activated = set_activated, | ||
156 | .set_fullscreen = set_fullscreen, | ||
157 | .for_each_surface = for_each_surface, | ||
158 | .close = _close, | ||
159 | .destroy = destroy, | ||
160 | }; | ||
161 | |||
162 | static void handle_commit(struct wl_listener *listener, void *data) { | ||
163 | struct sway_xdg_shell_view *xdg_shell_view = | ||
164 | wl_container_of(listener, xdg_shell_view, commit); | ||
165 | struct sway_view *view = &xdg_shell_view->view; | ||
166 | // NOTE: We intentionally discard the view's desired width here | ||
167 | // TODO: Store this for restoration when moving to floating plane | ||
168 | // TODO: Let floating views do whatever | ||
169 | view_update_size(view, xdg_shell_view->pending_width, | ||
170 | xdg_shell_view->pending_height); | ||
171 | view_update_title(view, false); | ||
172 | view_damage_from(view); | ||
173 | } | ||
174 | |||
175 | static void handle_new_popup(struct wl_listener *listener, void *data) { | ||
176 | struct sway_xdg_shell_view *xdg_shell_view = | ||
177 | wl_container_of(listener, xdg_shell_view, new_popup); | ||
178 | struct wlr_xdg_popup *wlr_popup = data; | ||
179 | popup_create(wlr_popup, &xdg_shell_view->view); | ||
180 | } | ||
181 | |||
182 | static void handle_unmap(struct wl_listener *listener, void *data) { | ||
183 | struct sway_xdg_shell_view *xdg_shell_view = | ||
184 | wl_container_of(listener, xdg_shell_view, unmap); | ||
185 | |||
186 | view_unmap(&xdg_shell_view->view); | ||
187 | |||
188 | wl_list_remove(&xdg_shell_view->commit.link); | ||
189 | wl_list_remove(&xdg_shell_view->new_popup.link); | ||
190 | } | ||
191 | |||
192 | static void handle_map(struct wl_listener *listener, void *data) { | ||
193 | struct sway_xdg_shell_view *xdg_shell_view = | ||
194 | wl_container_of(listener, xdg_shell_view, map); | ||
195 | struct sway_view *view = &xdg_shell_view->view; | ||
196 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | ||
197 | |||
198 | view_map(view, view->wlr_xdg_surface->surface); | ||
199 | |||
200 | xdg_shell_view->commit.notify = handle_commit; | ||
201 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
202 | &xdg_shell_view->commit); | ||
203 | |||
204 | xdg_shell_view->new_popup.notify = handle_new_popup; | ||
205 | wl_signal_add(&xdg_surface->events.new_popup, | ||
206 | &xdg_shell_view->new_popup); | ||
207 | |||
208 | if (xdg_surface->toplevel->client_pending.fullscreen) { | ||
209 | view_set_fullscreen(view, true); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void handle_destroy(struct wl_listener *listener, void *data) { | ||
214 | struct sway_xdg_shell_view *xdg_shell_view = | ||
215 | wl_container_of(listener, xdg_shell_view, destroy); | ||
216 | view_destroy(&xdg_shell_view->view); | ||
217 | } | ||
218 | |||
219 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | ||
220 | struct sway_xdg_shell_view *xdg_shell_view = | ||
221 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | ||
222 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | ||
223 | struct wlr_xdg_surface *xdg_surface = | ||
224 | xdg_shell_view->view.wlr_xdg_surface; | ||
225 | |||
226 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | ||
227 | "xdg_shell requested fullscreen of surface with role %i", | ||
228 | xdg_surface->role)) { | ||
229 | return; | ||
230 | } | ||
231 | if (!xdg_surface->mapped) { | ||
232 | return; | ||
233 | } | ||
234 | |||
235 | view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); | ||
236 | } | ||
237 | |||
238 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | ||
239 | struct sway_server *server = wl_container_of(listener, server, | ||
240 | xdg_shell_surface); | ||
241 | struct wlr_xdg_surface *xdg_surface = data; | ||
242 | |||
243 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
244 | wlr_log(L_DEBUG, "New xdg_shell popup"); | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | ||
249 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | ||
250 | wlr_xdg_surface_ping(xdg_surface); | ||
251 | wlr_xdg_toplevel_set_maximized(xdg_surface, true); | ||
252 | |||
253 | struct sway_xdg_shell_view *xdg_shell_view = | ||
254 | calloc(1, sizeof(struct sway_xdg_shell_view)); | ||
255 | if (!sway_assert(xdg_shell_view, "Failed to allocate view")) { | ||
256 | return; | ||
257 | } | ||
258 | |||
259 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | ||
260 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | ||
261 | |||
262 | // TODO: | ||
263 | // - Look up pid and open on appropriate workspace | ||
264 | |||
265 | xdg_shell_view->map.notify = handle_map; | ||
266 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | ||
267 | |||
268 | xdg_shell_view->unmap.notify = handle_unmap; | ||
269 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); | ||
270 | |||
271 | xdg_shell_view->destroy.notify = handle_destroy; | ||
272 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); | ||
273 | |||
274 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
275 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
276 | &xdg_shell_view->request_fullscreen); | ||
277 | } | ||
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 51ce86e1..9259c475 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #endif | 7 | #endif |
8 | #include <wlr/types/wlr_cursor.h> | 8 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_xcursor_manager.h> | 9 | #include <wlr/types/wlr_xcursor_manager.h> |
10 | #include <wlr/types/wlr_idle.h> | ||
10 | #include "list.h" | 11 | #include "list.h" |
11 | #include "log.h" | 12 | #include "log.h" |
12 | #include "sway/input/cursor.h" | 13 | #include "sway/input/cursor.h" |
@@ -172,6 +173,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) | |||
172 | 173 | ||
173 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { | 174 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { |
174 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); | 175 | struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); |
176 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
175 | struct wlr_event_pointer_motion *event = data; | 177 | struct wlr_event_pointer_motion *event = data; |
176 | wlr_cursor_move(cursor->cursor, event->device, | 178 | wlr_cursor_move(cursor->cursor, event->device, |
177 | event->delta_x, event->delta_y); | 179 | event->delta_x, event->delta_y); |
@@ -182,6 +184,7 @@ static void handle_cursor_motion_absolute( | |||
182 | struct wl_listener *listener, void *data) { | 184 | struct wl_listener *listener, void *data) { |
183 | struct sway_cursor *cursor = | 185 | struct sway_cursor *cursor = |
184 | wl_container_of(listener, cursor, motion_absolute); | 186 | wl_container_of(listener, cursor, motion_absolute); |
187 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
185 | struct wlr_event_pointer_motion_absolute *event = data; | 188 | struct wlr_event_pointer_motion_absolute *event = data; |
186 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); | 189 | wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); |
187 | cursor_send_pointer_motion(cursor, event->time_msec); | 190 | cursor_send_pointer_motion(cursor, event->time_msec); |
@@ -231,6 +234,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
231 | 234 | ||
232 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 235 | static void handle_cursor_button(struct wl_listener *listener, void *data) { |
233 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); | 236 | struct sway_cursor *cursor = wl_container_of(listener, cursor, button); |
237 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
234 | struct wlr_event_pointer_button *event = data; | 238 | struct wlr_event_pointer_button *event = data; |
235 | dispatch_cursor_button(cursor, | 239 | dispatch_cursor_button(cursor, |
236 | event->time_msec, event->button, event->state); | 240 | event->time_msec, event->button, event->state); |
@@ -238,6 +242,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { | |||
238 | 242 | ||
239 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { | 243 | static void handle_cursor_axis(struct wl_listener *listener, void *data) { |
240 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); | 244 | struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); |
245 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
241 | struct wlr_event_pointer_axis *event = data; | 246 | struct wlr_event_pointer_axis *event = data; |
242 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, | 247 | wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, |
243 | event->orientation, event->delta, event->delta_discrete, event->source); | 248 | event->orientation, event->delta, event->delta_discrete, event->source); |
@@ -245,6 +250,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) { | |||
245 | 250 | ||
246 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 251 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
247 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 252 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
253 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
248 | struct wlr_event_touch_down *event = data; | 254 | struct wlr_event_touch_down *event = data; |
249 | 255 | ||
250 | struct wlr_seat *seat = cursor->seat->wlr_seat; | 256 | struct wlr_seat *seat = cursor->seat->wlr_seat; |
@@ -271,6 +277,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
271 | 277 | ||
272 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 278 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
273 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 279 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); |
280 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
274 | struct wlr_event_touch_up *event = data; | 281 | struct wlr_event_touch_up *event = data; |
275 | struct wlr_seat *seat = cursor->seat->wlr_seat; | 282 | struct wlr_seat *seat = cursor->seat->wlr_seat; |
276 | // TODO: fall back to cursor simulation if client has not bound to touch | 283 | // TODO: fall back to cursor simulation if client has not bound to touch |
@@ -280,6 +287,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { | |||
280 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 287 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
281 | struct sway_cursor *cursor = | 288 | struct sway_cursor *cursor = |
282 | wl_container_of(listener, cursor, touch_motion); | 289 | wl_container_of(listener, cursor, touch_motion); |
290 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
283 | struct wlr_event_touch_motion *event = data; | 291 | struct wlr_event_touch_motion *event = data; |
284 | 292 | ||
285 | struct wlr_seat *seat = cursor->seat->wlr_seat; | 293 | struct wlr_seat *seat = cursor->seat->wlr_seat; |
@@ -331,6 +339,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device, | |||
331 | 339 | ||
332 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 340 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
333 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 341 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
342 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
334 | struct wlr_event_tablet_tool_axis *event = data; | 343 | struct wlr_event_tablet_tool_axis *event = data; |
335 | struct sway_input_device *input_device = event->device->data; | 344 | struct sway_input_device *input_device = event->device->data; |
336 | 345 | ||
@@ -353,6 +362,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
353 | 362 | ||
354 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 363 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
355 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 364 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
365 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
356 | struct wlr_event_tablet_tool_tip *event = data; | 366 | struct wlr_event_tablet_tool_tip *event = data; |
357 | dispatch_cursor_button(cursor, event->time_msec, | 367 | dispatch_cursor_button(cursor, event->time_msec, |
358 | BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? | 368 | BTN_LEFT, event->state == WLR_TABLET_TOOL_TIP_DOWN ? |
@@ -361,6 +371,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { | |||
361 | 371 | ||
362 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 372 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
363 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 373 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
374 | wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); | ||
364 | struct wlr_event_tablet_tool_button *event = data; | 375 | struct wlr_event_tablet_tool_button *event = data; |
365 | // TODO: the user may want to configure which tool buttons are mapped to | 376 | // TODO: the user may want to configure which tool buttons are mapped to |
366 | // which simulated pointer buttons | 377 | // which simulated pointer buttons |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index dbf2ce01..c07557db 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <limits.h> | 2 | #include <limits.h> |
3 | #include <wlr/backend/multi.h> | 3 | #include <wlr/backend/multi.h> |
4 | #include <wlr/backend/session.h> | 4 | #include <wlr/backend/session.h> |
5 | #include <wlr/types/wlr_idle.h> | ||
5 | #include "sway/input/seat.h" | 6 | #include "sway/input/seat.h" |
6 | #include "sway/input/keyboard.h" | 7 | #include "sway/input/keyboard.h" |
7 | #include "sway/input/input-manager.h" | 8 | #include "sway/input/input-manager.h" |
@@ -330,6 +331,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
330 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 331 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
331 | struct wlr_input_device *wlr_device = | 332 | struct wlr_input_device *wlr_device = |
332 | keyboard->seat_device->input_device->wlr_device; | 333 | keyboard->seat_device->input_device->wlr_device; |
334 | wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); | ||
333 | struct wlr_event_keyboard_key *event = data; | 335 | struct wlr_event_keyboard_key *event = data; |
334 | 336 | ||
335 | xkb_keycode_t keycode = event->keycode + 8; | 337 | xkb_keycode_t keycode = event->keycode + 8; |
diff --git a/sway/meson.build b/sway/meson.build index acf4a3e4..570d4783 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -10,10 +10,11 @@ sway_sources = files( | |||
10 | 'security.c', | 10 | 'security.c', |
11 | 11 | ||
12 | 'desktop/desktop.c', | 12 | 'desktop/desktop.c', |
13 | 'desktop/output.c', | ||
14 | 'desktop/layer_shell.c', | 13 | 'desktop/layer_shell.c', |
14 | 'desktop/output.c', | ||
15 | 'desktop/wl_shell.c', | 15 | 'desktop/wl_shell.c', |
16 | 'desktop/xdg_shell_v6.c', | 16 | 'desktop/xdg_shell_v6.c', |
17 | 'desktop/xdg_shell.c', | ||
17 | 'desktop/xwayland.c', | 18 | 'desktop/xwayland.c', |
18 | 19 | ||
19 | 'input/input-manager.c', | 20 | 'input/input-manager.c', |
diff --git a/sway/server.c b/sway/server.c index 8c41ee87..6147a39d 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <wlr/types/wlr_xcursor_manager.h> | 16 | #include <wlr/types/wlr_xcursor_manager.h> |
17 | #include <wlr/types/wlr_xdg_output.h> | 17 | #include <wlr/types/wlr_xdg_output.h> |
18 | #include <wlr/types/wlr_wl_shell.h> | 18 | #include <wlr/types/wlr_wl_shell.h> |
19 | #include <wlr/types/wlr_idle.h> | ||
19 | #include <wlr/util/log.h> | 20 | #include <wlr/util/log.h> |
20 | // TODO WLR: make Xwayland optional | 21 | // TODO WLR: make Xwayland optional |
21 | #include <wlr/xwayland.h> | 22 | #include <wlr/xwayland.h> |
@@ -61,6 +62,7 @@ bool server_init(struct sway_server *server) { | |||
61 | server->data_device_manager = | 62 | server->data_device_manager = |
62 | wlr_data_device_manager_create(server->wl_display); | 63 | wlr_data_device_manager_create(server->wl_display); |
63 | 64 | ||
65 | server->idle = wlr_idle_create(server->wl_display); | ||
64 | wlr_screenshooter_create(server->wl_display); | 66 | wlr_screenshooter_create(server->wl_display); |
65 | wlr_gamma_control_manager_create(server->wl_display); | 67 | wlr_gamma_control_manager_create(server->wl_display); |
66 | wlr_primary_selection_device_manager_create(server->wl_display); | 68 | wlr_primary_selection_device_manager_create(server->wl_display); |
@@ -81,6 +83,11 @@ bool server_init(struct sway_server *server) { | |||
81 | &server->xdg_shell_v6_surface); | 83 | &server->xdg_shell_v6_surface); |
82 | server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; | 84 | server->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; |
83 | 85 | ||
86 | server->xdg_shell = wlr_xdg_shell_create(server->wl_display); | ||
87 | wl_signal_add(&server->xdg_shell->events.new_surface, | ||
88 | &server->xdg_shell_surface); | ||
89 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | ||
90 | |||
84 | server->wl_shell = wlr_wl_shell_create(server->wl_display); | 91 | server->wl_shell = wlr_wl_shell_create(server->wl_display); |
85 | wl_signal_add(&server->wl_shell->events.new_surface, | 92 | wl_signal_add(&server->wl_shell->events.new_surface, |
86 | &server->wl_shell_surface); | 93 | &server->wl_shell_surface); |
@@ -88,7 +95,7 @@ bool server_init(struct sway_server *server) { | |||
88 | 95 | ||
89 | // TODO make xwayland optional | 96 | // TODO make xwayland optional |
90 | server->xwayland = | 97 | server->xwayland = |
91 | wlr_xwayland_create(server->wl_display, server->compositor, false); | 98 | wlr_xwayland_create(server->wl_display, server->compositor, true); |
92 | wl_signal_add(&server->xwayland->events.new_surface, | 99 | wl_signal_add(&server->xwayland->events.new_surface, |
93 | &server->xwayland_surface); | 100 | &server->xwayland_surface); |
94 | server->xwayland_surface.notify = handle_xwayland_surface; | 101 | server->xwayland_surface.notify = handle_xwayland_surface; |
diff --git a/sway/tree/container.c b/sway/tree/container.c index fc35a81c..9f7294db 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <wayland-server.h> | 7 | #include <wayland-server.h> |
8 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
9 | #include <wlr/types/wlr_wl_shell.h> | 9 | #include <wlr/types/wlr_wl_shell.h> |
10 | #include <wlr/types/wlr_xdg_shell_v6.h> | ||
11 | #include <wlr/types/wlr_xdg_shell.h> | ||
10 | #include "cairo.h" | 12 | #include "cairo.h" |
11 | #include "pango.h" | 13 | #include "pango.h" |
12 | #include "sway/config.h" | 14 | #include "sway/config.h" |
@@ -459,6 +461,16 @@ struct sway_container *container_at(struct sway_container *parent, | |||
459 | sview->wlr_xdg_surface_v6, | 461 | sview->wlr_xdg_surface_v6, |
460 | view_sx, view_sy, &_sx, &_sy); | 462 | view_sx, view_sy, &_sx, &_sy); |
461 | break; | 463 | break; |
464 | case SWAY_VIEW_XDG_SHELL: | ||
465 | // the top left corner of the sway container is the | ||
466 | // coordinate of the top left corner of the window geometry | ||
467 | view_sx += sview->wlr_xdg_surface->geometry.x; | ||
468 | view_sy += sview->wlr_xdg_surface->geometry.y; | ||
469 | |||
470 | _surface = wlr_xdg_surface_surface_at( | ||
471 | sview->wlr_xdg_surface, | ||
472 | view_sx, view_sy, &_sx, &_sy); | ||
473 | break; | ||
462 | } | 474 | } |
463 | if (_surface) { | 475 | if (_surface) { |
464 | *sx = _sx; | 476 | *sx = _sx; |
diff --git a/sway/tree/view.c b/sway/tree/view.c index a48a6619..a485e902 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -14,6 +14,8 @@ | |||
14 | #include "sway/tree/layout.h" | 14 | #include "sway/tree/layout.h" |
15 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
16 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
17 | #include "sway/config.h" | ||
18 | #include "pango.h" | ||
17 | 19 | ||
18 | void view_init(struct sway_view *view, enum sway_view_type type, | 20 | void view_init(struct sway_view *view, enum sway_view_type type, |
19 | const struct sway_view_impl *impl) { | 21 | const struct sway_view_impl *impl) { |
@@ -74,6 +76,8 @@ const char *view_get_type(struct sway_view *view) { | |||
74 | return "wl_shell"; | 76 | return "wl_shell"; |
75 | case SWAY_VIEW_XDG_SHELL_V6: | 77 | case SWAY_VIEW_XDG_SHELL_V6: |
76 | return "xdg_shell_v6"; | 78 | return "xdg_shell_v6"; |
79 | case SWAY_VIEW_XDG_SHELL: | ||
80 | return "xdg_shell"; | ||
77 | case SWAY_VIEW_XWAYLAND: | 81 | case SWAY_VIEW_XWAYLAND: |
78 | return "xwayland"; | 82 | return "xwayland"; |
79 | } | 83 | } |
@@ -607,6 +611,19 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
607 | return len; | 611 | return len; |
608 | } | 612 | } |
609 | 613 | ||
614 | static char *escape_title(char *buffer) { | ||
615 | int length = escape_markup_text(buffer, NULL, 0); | ||
616 | char *escaped_title = calloc(length + 1, sizeof(char)); | ||
617 | int result = escape_markup_text(buffer, escaped_title, length); | ||
618 | if (result != length) { | ||
619 | wlr_log(L_ERROR, "Could not escape title: %s", buffer); | ||
620 | free(escaped_title); | ||
621 | return buffer; | ||
622 | } | ||
623 | free(buffer); | ||
624 | return escaped_title; | ||
625 | } | ||
626 | |||
610 | void view_update_title(struct sway_view *view, bool force) { | 627 | void view_update_title(struct sway_view *view, bool force) { |
611 | if (!view->swayc) { | 628 | if (!view->swayc) { |
612 | return; | 629 | return; |
@@ -626,11 +643,15 @@ void view_update_title(struct sway_view *view, bool force) { | |||
626 | free(view->swayc->formatted_title); | 643 | free(view->swayc->formatted_title); |
627 | if (title) { | 644 | if (title) { |
628 | size_t len = parse_title_format(view, NULL); | 645 | size_t len = parse_title_format(view, NULL); |
629 | char *buffer = calloc(len + 1, 1); | 646 | char *buffer = calloc(len + 1, sizeof(char)); |
630 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 647 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
631 | return; | 648 | return; |
632 | } | 649 | } |
633 | parse_title_format(view, buffer); | 650 | parse_title_format(view, buffer); |
651 | // now we have the title, but needs to be escaped when using pango markup | ||
652 | if (config->pango_markup) { | ||
653 | buffer = escape_title(buffer); | ||
654 | } | ||
634 | 655 | ||
635 | view->swayc->name = strdup(title); | 656 | view->swayc->name = strdup(title); |
636 | view->swayc->formatted_title = buffer; | 657 | view->swayc->formatted_title = buffer; |
diff --git a/swayidle/main.c b/swayidle/main.c new file mode 100644 index 00000000..60235280 --- /dev/null +++ b/swayidle/main.c | |||
@@ -0,0 +1,415 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <getopt.h> | ||
3 | #include <signal.h> | ||
4 | #include <pthread.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <errno.h> | ||
8 | #include <string.h> | ||
9 | #include <unistd.h> | ||
10 | #include <wayland-client-protocol.h> | ||
11 | #include <wayland-client.h> | ||
12 | #include <wayland-util.h> | ||
13 | #include <wlr/config.h> | ||
14 | #include <wlr/util/log.h> | ||
15 | #include <wlr/types/wlr_output_layout.h> | ||
16 | #include <wlr/types/wlr_output.h> | ||
17 | #include "idle-client-protocol.h" | ||
18 | #include "config.h" | ||
19 | #include "list.h" | ||
20 | #ifdef SWAY_IDLE_HAS_SYSTEMD | ||
21 | #include <systemd/sd-bus.h> | ||
22 | #include <systemd/sd-login.h> | ||
23 | #elif defined(SWAY_IDLE_HAS_ELOGIND) | ||
24 | #include <elogind/sd-bus.h> | ||
25 | #include <elogind/sd-login.h> | ||
26 | #endif | ||
27 | |||
28 | typedef void (*timer_callback_func)(void *data); | ||
29 | |||
30 | static struct org_kde_kwin_idle *idle_manager = NULL; | ||
31 | static struct wl_seat *seat = NULL; | ||
32 | bool debug = false; | ||
33 | |||
34 | struct swayidle_state { | ||
35 | struct wl_display *display; | ||
36 | struct org_kde_kwin_idle_timeout *idle_timer; | ||
37 | struct org_kde_kwin_idle_timeout *lock_timer; | ||
38 | struct wlr_output_layout *layout; | ||
39 | struct wl_event_loop *event_loop; | ||
40 | list_t *timeout_cmds; | ||
41 | } state; | ||
42 | |||
43 | struct swayidle_cmd { | ||
44 | timer_callback_func callback; | ||
45 | char *param; | ||
46 | }; | ||
47 | |||
48 | struct swayidle_cmd *lock_cmd = NULL; | ||
49 | |||
50 | struct swayidle_timeout_cmd { | ||
51 | uint32_t timeout; | ||
52 | struct swayidle_cmd *idle_cmd; | ||
53 | struct swayidle_cmd *resume_cmd; | ||
54 | }; | ||
55 | |||
56 | static void cmd_exec(void *data) { | ||
57 | if (data == NULL) { | ||
58 | return; | ||
59 | } | ||
60 | char *param = (char *)data; | ||
61 | wlr_log(L_DEBUG, "Cmd exec %s", param); | ||
62 | int pid = fork(); | ||
63 | if (pid == 0) { | ||
64 | char *const cmd[] = { "sh", "-c", param, NULL, }; | ||
65 | execvp(cmd[0], cmd); | ||
66 | exit(1); | ||
67 | } | ||
68 | wlr_log(L_DEBUG, "Spawned process %d", pid); | ||
69 | } | ||
70 | |||
71 | #if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) | ||
72 | static int lock_fd = -1; | ||
73 | static int ongoing_fd = -1; | ||
74 | |||
75 | static int release_lock(void *data) { | ||
76 | wlr_log(L_INFO, "Releasing sleep lock %d", ongoing_fd); | ||
77 | if (ongoing_fd >= 0) { | ||
78 | close(ongoing_fd); | ||
79 | } | ||
80 | ongoing_fd = -1; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | void acquire_sleep_lock() { | ||
85 | sd_bus_message *msg = NULL; | ||
86 | sd_bus_error error = SD_BUS_ERROR_NULL; | ||
87 | struct sd_bus *bus; | ||
88 | int ret = sd_bus_default_system(&bus); | ||
89 | |||
90 | if (ret < 0) { | ||
91 | wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", | ||
92 | strerror(-ret)); | ||
93 | return; | ||
94 | } | ||
95 | |||
96 | ret = sd_bus_call_method(bus, "org.freedesktop.login1", | ||
97 | "/org/freedesktop/login1", | ||
98 | "org.freedesktop.login1.Manager", "Inhibit", | ||
99 | &error, &msg, "ssss", "sleep", "swayidle", | ||
100 | "Setup Up Lock Screen", "delay"); | ||
101 | if (ret < 0) { | ||
102 | wlr_log(L_ERROR, "Failed to send Inhibit signal: %s", | ||
103 | strerror(-ret)); | ||
104 | } else { | ||
105 | ret = sd_bus_message_read(msg, "h", &lock_fd); | ||
106 | if (ret < 0) { | ||
107 | wlr_log(L_ERROR, | ||
108 | "Failed to parse D-Bus response for Inhibit: %s", | ||
109 | strerror(-ret)); | ||
110 | } | ||
111 | } | ||
112 | wlr_log(L_INFO, "Got sleep lock: %d", lock_fd); | ||
113 | } | ||
114 | |||
115 | static int prepare_for_sleep(sd_bus_message *msg, void *userdata, | ||
116 | sd_bus_error *ret_error) { | ||
117 | bool going_down = true; | ||
118 | int ret = sd_bus_message_read(msg, "b", &going_down); | ||
119 | if (ret < 0) { | ||
120 | wlr_log(L_ERROR, "Failed to parse D-Bus response for Inhibit: %s", | ||
121 | strerror(-ret)); | ||
122 | } | ||
123 | wlr_log(L_DEBUG, "PrepareForSleep signal received %d", going_down); | ||
124 | if (!going_down) { | ||
125 | acquire_sleep_lock(); | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | ongoing_fd = lock_fd; | ||
130 | |||
131 | if (lock_cmd && lock_cmd->callback) { | ||
132 | lock_cmd->callback(lock_cmd->param); | ||
133 | } | ||
134 | |||
135 | if (ongoing_fd >= 0) { | ||
136 | struct wl_event_source *source = | ||
137 | wl_event_loop_add_timer(state.event_loop, release_lock, NULL); | ||
138 | wl_event_source_timer_update(source, 1000); | ||
139 | } | ||
140 | wlr_log(L_DEBUG, "Prepare for sleep done"); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int dbus_event(int fd, uint32_t mask, void *data) { | ||
145 | sd_bus *bus = data; | ||
146 | while (sd_bus_process(bus, NULL) > 0) { | ||
147 | // Do nothing. | ||
148 | } | ||
149 | return 1; | ||
150 | } | ||
151 | |||
152 | void setup_sleep_listener() { | ||
153 | struct sd_bus *bus; | ||
154 | |||
155 | int ret = sd_bus_default_system(&bus); | ||
156 | if (ret < 0) { | ||
157 | wlr_log(L_ERROR, "Failed to open D-Bus connection: %s", | ||
158 | strerror(-ret)); | ||
159 | return; | ||
160 | } | ||
161 | |||
162 | char str[256]; | ||
163 | const char *fmt = "type='signal'," | ||
164 | "sender='org.freedesktop.login1'," | ||
165 | "interface='org.freedesktop.login1.%s'," | ||
166 | "member='%s'," "path='%s'"; | ||
167 | |||
168 | snprintf(str, sizeof(str), fmt, "Manager", "PrepareForSleep", | ||
169 | "/org/freedesktop/login1"); | ||
170 | ret = sd_bus_add_match(bus, NULL, str, prepare_for_sleep, NULL); | ||
171 | if (ret < 0) { | ||
172 | wlr_log(L_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); | ||
173 | return; | ||
174 | } | ||
175 | acquire_sleep_lock(); | ||
176 | |||
177 | wl_event_loop_add_fd(state.event_loop, sd_bus_get_fd(bus), | ||
178 | WL_EVENT_READABLE, dbus_event, bus); | ||
179 | } | ||
180 | #endif | ||
181 | |||
182 | static void handle_global(void *data, struct wl_registry *registry, | ||
183 | uint32_t name, const char *interface, uint32_t version) { | ||
184 | if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0) { | ||
185 | idle_manager = | ||
186 | wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1); | ||
187 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { | ||
188 | seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void handle_global_remove(void *data, struct wl_registry *registry, | ||
193 | uint32_t name) { | ||
194 | } | ||
195 | |||
196 | static const struct wl_registry_listener registry_listener = { | ||
197 | .global = handle_global, | ||
198 | .global_remove = handle_global_remove, | ||
199 | }; | ||
200 | |||
201 | static void handle_idle(void *data, struct org_kde_kwin_idle_timeout *timer) { | ||
202 | struct swayidle_timeout_cmd *cmd = data; | ||
203 | wlr_log(L_DEBUG, "idle state"); | ||
204 | if (cmd && cmd->idle_cmd && cmd->idle_cmd->callback) { | ||
205 | cmd->idle_cmd->callback(cmd->idle_cmd->param); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void handle_resume(void *data, struct org_kde_kwin_idle_timeout *timer) { | ||
210 | struct swayidle_timeout_cmd *cmd = data; | ||
211 | wlr_log(L_DEBUG, "active state"); | ||
212 | if (cmd && cmd->resume_cmd && cmd->resume_cmd->callback) { | ||
213 | cmd->resume_cmd->callback(cmd->resume_cmd->param); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = { | ||
218 | .idle = handle_idle, | ||
219 | .resumed = handle_resume, | ||
220 | }; | ||
221 | |||
222 | struct swayidle_cmd *parse_command(int argc, char **argv) { | ||
223 | if (argc < 1) { | ||
224 | wlr_log(L_ERROR, "Too few parameters for command in parse_command"); | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | struct swayidle_cmd *cmd = calloc(1, sizeof(struct swayidle_cmd)); | ||
229 | wlr_log(L_DEBUG, "Command: %s", argv[0]); | ||
230 | cmd->callback = cmd_exec; | ||
231 | cmd->param = argv[0]; | ||
232 | return cmd; | ||
233 | } | ||
234 | |||
235 | int parse_timeout(int argc, char **argv) { | ||
236 | if (argc < 3) { | ||
237 | wlr_log(L_ERROR, "Too few parameters to timeout command. " | ||
238 | "Usage: timeout <seconds> <command>"); | ||
239 | exit(-1); | ||
240 | } | ||
241 | errno = 0; | ||
242 | char *endptr; | ||
243 | int seconds = strtoul(argv[1], &endptr, 10); | ||
244 | if (errno != 0 || *endptr != '\0') { | ||
245 | wlr_log(L_ERROR, "Invalid timeout parameter '%s', it should be a " | ||
246 | "numeric value representing seconds", optarg); | ||
247 | exit(-1); | ||
248 | } | ||
249 | struct swayidle_timeout_cmd *cmd = | ||
250 | calloc(1, sizeof(struct swayidle_timeout_cmd)); | ||
251 | cmd->timeout = seconds * 1000; | ||
252 | |||
253 | wlr_log(L_DEBUG, "Register idle timeout at %d ms", cmd->timeout); | ||
254 | wlr_log(L_DEBUG, "Setup idle"); | ||
255 | cmd->idle_cmd = parse_command(argc - 2, &argv[2]); | ||
256 | |||
257 | int result = 3; | ||
258 | if (argc >= 5 && !strcmp("resume", argv[3])) { | ||
259 | wlr_log(L_DEBUG, "Setup resume"); | ||
260 | cmd->resume_cmd = parse_command(argc - 4, &argv[4]); | ||
261 | result = 5; | ||
262 | } | ||
263 | list_add(state.timeout_cmds, cmd); | ||
264 | return result; | ||
265 | } | ||
266 | |||
267 | int parse_sleep(int argc, char **argv) { | ||
268 | if (argc < 2) { | ||
269 | wlr_log(L_ERROR, "Too few parameters to before-sleep command. " | ||
270 | "Usage: before-sleep <command>"); | ||
271 | exit(-1); | ||
272 | } | ||
273 | |||
274 | lock_cmd = parse_command(argc - 1, &argv[1]); | ||
275 | if (lock_cmd) { | ||
276 | wlr_log(L_DEBUG, "Setup sleep lock: %s", lock_cmd->param); | ||
277 | } | ||
278 | |||
279 | return 2; | ||
280 | } | ||
281 | |||
282 | |||
283 | int parse_args(int argc, char *argv[]) { | ||
284 | int c; | ||
285 | |||
286 | while ((c = getopt(argc, argv, "hs:d")) != -1) { | ||
287 | switch(c) { | ||
288 | case 'd': | ||
289 | debug = true; | ||
290 | break; | ||
291 | case 'h': | ||
292 | case '?': | ||
293 | printf("Usage: %s [OPTIONS]\n", argv[0]); | ||
294 | printf(" -d\tdebug\n"); | ||
295 | printf(" -h\tthis help menu\n"); | ||
296 | return 1; | ||
297 | default: | ||
298 | return 1; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | if (debug) { | ||
303 | wlr_log_init(L_DEBUG, NULL); | ||
304 | wlr_log(L_DEBUG, "Loglevel debug"); | ||
305 | } else { | ||
306 | wlr_log_init(L_INFO, NULL); | ||
307 | } | ||
308 | |||
309 | |||
310 | state.timeout_cmds = create_list(); | ||
311 | |||
312 | int i = optind; | ||
313 | while (i < argc) { | ||
314 | if (!strcmp("timeout", argv[i])) { | ||
315 | wlr_log(L_DEBUG, "Got timeout"); | ||
316 | i += parse_timeout(argc - i, &argv[i]); | ||
317 | } else if (!strcmp("before-sleep", argv[i])) { | ||
318 | wlr_log(L_DEBUG, "Got before-sleep"); | ||
319 | i += parse_sleep(argc - i, &argv[i]); | ||
320 | } else { | ||
321 | wlr_log(L_ERROR, "Unsupported command '%s'", argv[i]); | ||
322 | exit(-1); | ||
323 | } | ||
324 | } | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | void sway_terminate(int exit_code) { | ||
329 | if (state.event_loop) { | ||
330 | wl_event_loop_destroy(state.event_loop); | ||
331 | } | ||
332 | if (state.display) { | ||
333 | wl_display_disconnect(state.display); | ||
334 | } | ||
335 | exit(exit_code); | ||
336 | } | ||
337 | |||
338 | void sig_handler(int signal) { | ||
339 | sway_terminate(0); | ||
340 | } | ||
341 | |||
342 | static int display_event(int fd, uint32_t mask, void *data) { | ||
343 | wl_display_dispatch(state.display); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | void register_idle_timeout(void *item) { | ||
348 | struct swayidle_timeout_cmd *cmd = item; | ||
349 | if (cmd == NULL || !cmd->timeout) { | ||
350 | wlr_log(L_ERROR, "Invalid idle cmd, will not register"); | ||
351 | return; | ||
352 | } | ||
353 | state.idle_timer = | ||
354 | org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, cmd->timeout); | ||
355 | if (state.idle_timer != NULL) { | ||
356 | org_kde_kwin_idle_timeout_add_listener(state.idle_timer, | ||
357 | &idle_timer_listener, cmd); | ||
358 | } else { | ||
359 | wlr_log(L_ERROR, "Could not create idle timer"); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | int main(int argc, char *argv[]) { | ||
364 | signal(SIGINT, sig_handler); | ||
365 | signal(SIGTERM, sig_handler); | ||
366 | |||
367 | if (parse_args(argc, argv) != 0) { | ||
368 | return -1; | ||
369 | } | ||
370 | |||
371 | state.display = wl_display_connect(NULL); | ||
372 | if (state.display == NULL) { | ||
373 | wlr_log(L_ERROR, "Failed to create display"); | ||
374 | return -3; | ||
375 | } | ||
376 | |||
377 | struct wl_registry *registry = wl_display_get_registry(state.display); | ||
378 | wl_registry_add_listener(registry, ®istry_listener, NULL); | ||
379 | wl_display_roundtrip(state.display); | ||
380 | state.layout = wlr_output_layout_create(); | ||
381 | state.event_loop = wl_event_loop_create(); | ||
382 | |||
383 | if (idle_manager == NULL) { | ||
384 | wlr_log(L_ERROR, "Display doesn't support idle protocol"); | ||
385 | return -4; | ||
386 | } | ||
387 | if (seat == NULL) { | ||
388 | wlr_log(L_ERROR, "Seat error"); | ||
389 | return -5; | ||
390 | } | ||
391 | |||
392 | bool should_run = state.timeout_cmds->length > 0; | ||
393 | #if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) | ||
394 | if (lock_cmd) { | ||
395 | should_run = true; | ||
396 | setup_sleep_listener(); | ||
397 | } | ||
398 | #endif | ||
399 | if (!should_run) { | ||
400 | wlr_log(L_INFO, "No command specified! Nothing to do, will exit"); | ||
401 | sway_terminate(0); | ||
402 | } | ||
403 | list_foreach(state.timeout_cmds, register_idle_timeout); | ||
404 | |||
405 | wl_display_roundtrip(state.display); | ||
406 | |||
407 | wl_event_loop_add_fd(state.event_loop, wl_display_get_fd(state.display), | ||
408 | WL_EVENT_READABLE, display_event, NULL); | ||
409 | |||
410 | while (wl_event_loop_dispatch(state.event_loop, -1) != 1) { | ||
411 | // This space intentionally left blank | ||
412 | } | ||
413 | |||
414 | sway_terminate(0); | ||
415 | } | ||
diff --git a/swayidle/meson.build b/swayidle/meson.build new file mode 100644 index 00000000..f62545f8 --- /dev/null +++ b/swayidle/meson.build | |||
@@ -0,0 +1,18 @@ | |||
1 | threads = dependency('threads') | ||
2 | |||
3 | executable( | ||
4 | 'swayidle', [ | ||
5 | 'main.c', | ||
6 | ], | ||
7 | include_directories: [sway_inc], | ||
8 | dependencies: [ | ||
9 | client_protos, | ||
10 | pixman, | ||
11 | wayland_client, | ||
12 | wayland_server, | ||
13 | wlroots, | ||
14 | swayidle_deps, | ||
15 | ], | ||
16 | link_with: [lib_sway_common, lib_sway_client], | ||
17 | install: true | ||
18 | ) | ||
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd new file mode 100644 index 00000000..5cd4a7fd --- /dev/null +++ b/swayidle/swayidle.1.scd | |||
@@ -0,0 +1,61 @@ | |||
1 | swayidle (1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | swayidle - Idle manager for Wayland | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | *swayidle* [options] [events...] | ||
10 | |||
11 | # OPTIONS | ||
12 | |||
13 | *-h* | ||
14 | Show help message and quit. | ||
15 | |||
16 | *-d* | ||
17 | Enable debug output. | ||
18 | |||
19 | # DESCRIPTION | ||
20 | |||
21 | swayidle listens for idle activity on your Wayland compositor and executes tasks | ||
22 | on various idle-related events. You can specify any number of events at the | ||
23 | command line. | ||
24 | |||
25 | # EVENTS | ||
26 | |||
27 | *timeout* <timeout> <timeout command> [resume <resume command>] | ||
28 | Execute _timeout command_ if there is no activity for <timeout> seconds. | ||
29 | |||
30 | If you specify "resume <resume command>", _resume command_ will be run when | ||
31 | there is activity again. | ||
32 | |||
33 | *before-sleep* <command> | ||
34 | If built with systemd support, executes _command_ before systemd puts the | ||
35 | computer to sleep. | ||
36 | |||
37 | All commands are executed in a shell. | ||
38 | |||
39 | # EXAMPLE | ||
40 | |||
41 | ``` | ||
42 | swayidle \ | ||
43 | timeout 300 'swaylock -c 000000' \ | ||
44 | timeout 600 'swaymsg "output * dpms off"' \ | ||
45 | resume 'swaymsg "output * dpms on"' \ | ||
46 | before-sleep 'swaylock -c 000000' | ||
47 | ``` | ||
48 | |||
49 | This will lock your screen after 300 seconds of inactivity, then turn off your | ||
50 | displays after another 600 seconds, and turn your screens back on when resumed. | ||
51 | It will also lock your screen before your computer goes to sleep. | ||
52 | |||
53 | # AUTHORS | ||
54 | |||
55 | Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open | ||
56 | source contributors. For more information about sway development, see | ||
57 | https://github.com/swaywm/sway. | ||
58 | |||
59 | # SEE ALSO | ||
60 | |||
61 | *sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5) | ||