diff options
author | Tony Crisci <tony@dubstepdish.com> | 2018-04-04 22:36:09 -0400 |
---|---|---|
committer | Tony Crisci <tony@dubstepdish.com> | 2018-04-04 22:36:09 -0400 |
commit | 65f254f3fbc83d006d4ec29170ec8a8695345d6c (patch) | |
tree | 3044fb62120ca23499d31275076af50db09a9850 | |
parent | fix focus child (diff) | |
parent | Merge pull request #1732 from emersion/view-children (diff) | |
download | sway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.tar.gz sway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.tar.zst sway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.zip |
Merge branch 'wlroots' into fix-focus-inactive
33 files changed, 1353 insertions, 991 deletions
diff --git a/common/background-image.c b/common/background-image.c new file mode 100644 index 00000000..e5fb4433 --- /dev/null +++ b/common/background-image.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdbool.h> | ||
3 | #include <wlr/util/log.h> | ||
4 | #include "background-image.h" | ||
5 | #include "cairo.h" | ||
6 | |||
7 | enum background_mode parse_background_mode(const char *mode) { | ||
8 | if (strcmp(mode, "stretch") == 0) { | ||
9 | return BACKGROUND_MODE_STRETCH; | ||
10 | } else if (strcmp(mode, "fill") == 0) { | ||
11 | return BACKGROUND_MODE_FILL; | ||
12 | } else if (strcmp(mode, "fit") == 0) { | ||
13 | return BACKGROUND_MODE_FIT; | ||
14 | } else if (strcmp(mode, "center") == 0) { | ||
15 | return BACKGROUND_MODE_CENTER; | ||
16 | } else if (strcmp(mode, "tile") == 0) { | ||
17 | return BACKGROUND_MODE_TILE; | ||
18 | } else if (strcmp(mode, "solid_color") == 0) { | ||
19 | return BACKGROUND_MODE_SOLID_COLOR; | ||
20 | } | ||
21 | wlr_log(L_ERROR, "Unsupported background mode: %s", mode); | ||
22 | return BACKGROUND_MODE_INVALID; | ||
23 | } | ||
24 | |||
25 | cairo_surface_t *load_background_image(const char *path) { | ||
26 | cairo_surface_t *image; | ||
27 | #ifdef HAVE_GDK_PIXBUF | ||
28 | GError *err = NULL; | ||
29 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); | ||
30 | if (!pixbuf) { | ||
31 | wlr_log(L_ERROR, "Failed to load background image (%s).", | ||
32 | err->message); | ||
33 | return false; | ||
34 | } | ||
35 | image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); | ||
36 | g_object_unref(pixbuf); | ||
37 | #else | ||
38 | image = cairo_image_surface_create_from_png(path); | ||
39 | #endif //HAVE_GDK_PIXBUF | ||
40 | if (!image) { | ||
41 | wlr_log(L_ERROR, "Failed to read background image."); | ||
42 | return NULL; | ||
43 | } | ||
44 | if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { | ||
45 | wlr_log(L_ERROR, "Failed to read background image: %s." | ||
46 | #ifndef HAVE_GDK_PIXBUF | ||
47 | "\nSway was compiled without gdk_pixbuf support, so only" | ||
48 | "\nPNG images can be loaded. This is the likely cause." | ||
49 | #endif //HAVE_GDK_PIXBUF | ||
50 | , cairo_status_to_string(cairo_surface_status(image))); | ||
51 | return NULL; | ||
52 | } | ||
53 | return image; | ||
54 | } | ||
55 | |||
56 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, | ||
57 | enum background_mode mode, int buffer_width, int buffer_height) { | ||
58 | double width = cairo_image_surface_get_width(image); | ||
59 | double height = cairo_image_surface_get_height(image); | ||
60 | |||
61 | switch (mode) { | ||
62 | case BACKGROUND_MODE_STRETCH: | ||
63 | cairo_scale(cairo, | ||
64 | (double)buffer_width / width, | ||
65 | (double)buffer_height / height); | ||
66 | cairo_set_source_surface(cairo, image, 0, 0); | ||
67 | break; | ||
68 | case BACKGROUND_MODE_FILL: { | ||
69 | double window_ratio = (double)buffer_width / buffer_height; | ||
70 | double bg_ratio = width / height; | ||
71 | |||
72 | if (window_ratio > bg_ratio) { | ||
73 | double scale = (double)buffer_width / width; | ||
74 | cairo_scale(cairo, scale, scale); | ||
75 | cairo_set_source_surface(cairo, image, | ||
76 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
77 | } else { | ||
78 | double scale = (double)buffer_height / height; | ||
79 | cairo_scale(cairo, scale, scale); | ||
80 | cairo_set_source_surface(cairo, image, | ||
81 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
82 | } | ||
83 | break; | ||
84 | } | ||
85 | case BACKGROUND_MODE_FIT: { | ||
86 | double window_ratio = (double)buffer_width / buffer_height; | ||
87 | double bg_ratio = width / height; | ||
88 | |||
89 | if (window_ratio > bg_ratio) { | ||
90 | double scale = (double)buffer_height / height; | ||
91 | cairo_scale(cairo, scale, scale); | ||
92 | cairo_set_source_surface(cairo, image, | ||
93 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
94 | } else { | ||
95 | double scale = (double)buffer_width / width; | ||
96 | cairo_scale(cairo, scale, scale); | ||
97 | cairo_set_source_surface(cairo, image, | ||
98 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
99 | } | ||
100 | break; | ||
101 | } | ||
102 | case BACKGROUND_MODE_CENTER: | ||
103 | cairo_set_source_surface(cairo, image, | ||
104 | (double)buffer_width / 2 - width / 2, | ||
105 | (double)buffer_height / 2 - height / 2); | ||
106 | break; | ||
107 | case BACKGROUND_MODE_TILE: { | ||
108 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | ||
109 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); | ||
110 | cairo_set_source(cairo, pattern); | ||
111 | break; | ||
112 | } | ||
113 | case BACKGROUND_MODE_SOLID_COLOR: | ||
114 | case BACKGROUND_MODE_INVALID: | ||
115 | assert(0); | ||
116 | break; | ||
117 | } | ||
118 | cairo_paint(cairo); | ||
119 | } | ||
diff --git a/common/meson.build b/common/meson.build index 4ad47077..44a29508 100644 --- a/common/meson.build +++ b/common/meson.build | |||
@@ -1,17 +1,7 @@ | |||
1 | deps = [ | ||
2 | cairo, | ||
3 | pango, | ||
4 | pangocairo, | ||
5 | wlroots | ||
6 | ] | ||
7 | |||
8 | if gdk_pixbuf.found() | ||
9 | deps += [gdk_pixbuf] | ||
10 | endif | ||
11 | |||
12 | lib_sway_common = static_library( | 1 | lib_sway_common = static_library( |
13 | 'sway-common', | 2 | 'sway-common', |
14 | files( | 3 | files( |
4 | 'background-image.c', | ||
15 | 'cairo.c', | 5 | 'cairo.c', |
16 | 'ipc-client.c', | 6 | 'ipc-client.c', |
17 | 'log.c', | 7 | 'log.c', |
@@ -19,8 +9,15 @@ lib_sway_common = static_library( | |||
19 | 'pango.c', | 9 | 'pango.c', |
20 | 'readline.c', | 10 | 'readline.c', |
21 | 'stringop.c', | 11 | 'stringop.c', |
12 | 'unicode.c', | ||
22 | 'util.c' | 13 | 'util.c' |
23 | ), | 14 | ), |
24 | dependencies: deps, | 15 | dependencies: [ |
16 | cairo, | ||
17 | gdk_pixbuf, | ||
18 | pango, | ||
19 | pangocairo, | ||
20 | wlroots | ||
21 | ], | ||
25 | include_directories: sway_inc | 22 | include_directories: sway_inc |
26 | ) | 23 | ) |
diff --git a/common/unicode.c b/common/unicode.c new file mode 100644 index 00000000..38a9b48e --- /dev/null +++ b/common/unicode.c | |||
@@ -0,0 +1,101 @@ | |||
1 | #include <stdint.h> | ||
2 | #include <stddef.h> | ||
3 | #include "unicode.h" | ||
4 | |||
5 | size_t utf8_chsize(uint32_t ch) { | ||
6 | if (ch < 0x80) { | ||
7 | return 1; | ||
8 | } else if (ch < 0x800) { | ||
9 | return 2; | ||
10 | } else if (ch < 0x10000) { | ||
11 | return 3; | ||
12 | } | ||
13 | return 4; | ||
14 | } | ||
15 | |||
16 | static const uint8_t masks[] = { | ||
17 | 0x7F, | ||
18 | 0x1F, | ||
19 | 0x0F, | ||
20 | 0x07, | ||
21 | 0x03, | ||
22 | 0x01 | ||
23 | }; | ||
24 | |||
25 | uint32_t utf8_decode(const char **char_str) { | ||
26 | uint8_t **s = (uint8_t **)char_str; | ||
27 | |||
28 | uint32_t cp = 0; | ||
29 | if (**s < 128) { | ||
30 | // shortcut | ||
31 | cp = **s; | ||
32 | ++*s; | ||
33 | return cp; | ||
34 | } | ||
35 | int size = utf8_size((char *)*s); | ||
36 | if (size == -1) { | ||
37 | ++*s; | ||
38 | return UTF8_INVALID; | ||
39 | } | ||
40 | uint8_t mask = masks[size - 1]; | ||
41 | cp = **s & mask; | ||
42 | ++*s; | ||
43 | while (--size) { | ||
44 | cp <<= 6; | ||
45 | cp |= **s & 0x3f; | ||
46 | ++*s; | ||
47 | } | ||
48 | return cp; | ||
49 | } | ||
50 | |||
51 | size_t utf8_encode(char *str, uint32_t ch) { | ||
52 | size_t len = 0; | ||
53 | uint8_t first; | ||
54 | |||
55 | if (ch < 0x80) { | ||
56 | first = 0; | ||
57 | len = 1; | ||
58 | } else if (ch < 0x800) { | ||
59 | first = 0xc0; | ||
60 | len = 2; | ||
61 | } else if (ch < 0x10000) { | ||
62 | first = 0xe0; | ||
63 | len = 3; | ||
64 | } else { | ||
65 | first = 0xf0; | ||
66 | len = 4; | ||
67 | } | ||
68 | |||
69 | for (size_t i = len - 1; i > 0; --i) { | ||
70 | str[i] = (ch & 0x3f) | 0x80; | ||
71 | ch >>= 6; | ||
72 | } | ||
73 | |||
74 | str[0] = ch | first; | ||
75 | return len; | ||
76 | } | ||
77 | |||
78 | |||
79 | static const struct { | ||
80 | uint8_t mask; | ||
81 | uint8_t result; | ||
82 | int octets; | ||
83 | } sizes[] = { | ||
84 | { 0x80, 0x00, 1 }, | ||
85 | { 0xE0, 0xC0, 2 }, | ||
86 | { 0xF0, 0xE0, 3 }, | ||
87 | { 0xF8, 0xF0, 4 }, | ||
88 | { 0xFC, 0xF8, 5 }, | ||
89 | { 0xFE, 0xF8, 6 }, | ||
90 | { 0x80, 0x80, -1 }, | ||
91 | }; | ||
92 | |||
93 | int utf8_size(const char *s) { | ||
94 | uint8_t c = (uint8_t)*s; | ||
95 | for (size_t i = 0; i < sizeof(sizes) / 2; ++i) { | ||
96 | if ((c & sizes[i].mask) == sizes[i].result) { | ||
97 | return sizes[i].octets; | ||
98 | } | ||
99 | } | ||
100 | return -1; | ||
101 | } | ||
diff --git a/include/background-image.h b/include/background-image.h new file mode 100644 index 00000000..15935ffd --- /dev/null +++ b/include/background-image.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef _SWAY_BACKGROUND_IMAGE_H | ||
2 | #define _SWAY_BACKGROUND_IMAGE_H | ||
3 | #include "cairo.h" | ||
4 | |||
5 | enum background_mode { | ||
6 | BACKGROUND_MODE_STRETCH, | ||
7 | BACKGROUND_MODE_FILL, | ||
8 | BACKGROUND_MODE_FIT, | ||
9 | BACKGROUND_MODE_CENTER, | ||
10 | BACKGROUND_MODE_TILE, | ||
11 | BACKGROUND_MODE_SOLID_COLOR, | ||
12 | BACKGROUND_MODE_INVALID, | ||
13 | }; | ||
14 | |||
15 | enum background_mode parse_background_mode(const char *mode); | ||
16 | cairo_surface_t *load_background_image(const char *path); | ||
17 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, | ||
18 | enum background_mode mode, int buffer_width, int buffer_height); | ||
19 | |||
20 | #endif | ||
diff --git a/include/sway/commands.h b/include/sway/commands.h index 66f097ea..edb5a213 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -123,6 +123,7 @@ sway_cmd cmd_mark; | |||
123 | sway_cmd cmd_mode; | 123 | sway_cmd cmd_mode; |
124 | sway_cmd cmd_mouse_warping; | 124 | sway_cmd cmd_mouse_warping; |
125 | sway_cmd cmd_move; | 125 | sway_cmd cmd_move; |
126 | sway_cmd cmd_opacity; | ||
126 | sway_cmd cmd_new_float; | 127 | sway_cmd cmd_new_float; |
127 | sway_cmd cmd_new_window; | 128 | sway_cmd cmd_new_window; |
128 | sway_cmd cmd_no_focus; | 129 | sway_cmd cmd_no_focus; |
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 8e39a4a7..89a3ac71 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H | 1 | #ifndef _SWAY_INPUT_INPUT_MANAGER_H |
2 | #define _SWAY_INPUT_INPUT_MANAGER_H | 2 | #define _SWAY_INPUT_INPUT_MANAGER_H |
3 | #include <libinput.h> | 3 | #include <libinput.h> |
4 | #include <wlr/types/wlr_input_inhibitor.h> | ||
4 | #include "sway/server.h" | 5 | #include "sway/server.h" |
5 | #include "sway/config.h" | 6 | #include "sway/config.h" |
6 | #include "list.h" | 7 | #include "list.h" |
@@ -23,7 +24,11 @@ struct sway_input_manager { | |||
23 | struct wl_list devices; | 24 | struct wl_list devices; |
24 | struct wl_list seats; | 25 | struct wl_list seats; |
25 | 26 | ||
27 | struct wlr_input_inhibit_manager *inhibit; | ||
28 | |||
26 | struct wl_listener new_input; | 29 | struct wl_listener new_input; |
30 | struct wl_listener inhibit_activate; | ||
31 | struct wl_listener inhibit_deactivate; | ||
27 | }; | 32 | }; |
28 | 33 | ||
29 | struct sway_input_manager *input_manager_create(struct sway_server *server); | 34 | struct sway_input_manager *input_manager_create(struct sway_server *server); |
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 137fcd22..d1cfbe4c 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -32,6 +32,9 @@ struct sway_seat { | |||
32 | // If the focused layer is set, views cannot receive keyboard focus | 32 | // If the focused layer is set, views cannot receive keyboard focus |
33 | struct wlr_layer_surface *focused_layer; | 33 | struct wlr_layer_surface *focused_layer; |
34 | 34 | ||
35 | // If exclusive_client is set, no other clients will receive input events | ||
36 | struct wl_client *exclusive_client; | ||
37 | |||
35 | struct wl_listener focus_destroy; | 38 | struct wl_listener focus_destroy; |
36 | struct wl_listener new_container; | 39 | struct wl_listener new_container; |
37 | 40 | ||
@@ -64,6 +67,9 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
64 | void seat_set_focus_layer(struct sway_seat *seat, | 67 | void seat_set_focus_layer(struct sway_seat *seat, |
65 | struct wlr_layer_surface *layer); | 68 | struct wlr_layer_surface *layer); |
66 | 69 | ||
70 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
71 | struct wl_client *client); | ||
72 | |||
67 | struct sway_container *seat_get_focus(struct sway_seat *seat); | 73 | struct sway_container *seat_get_focus(struct sway_seat *seat); |
68 | 74 | ||
69 | /** | 75 | /** |
@@ -85,4 +91,6 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); | |||
85 | 91 | ||
86 | struct seat_config *seat_get_config(struct sway_seat *seat); | 92 | struct seat_config *seat_get_config(struct sway_seat *seat); |
87 | 93 | ||
94 | bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); | ||
95 | |||
88 | #endif | 96 | #endif |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 277165ea..3a3a9429 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -83,6 +83,8 @@ struct sway_container { | |||
83 | 83 | ||
84 | list_t *marks; // list of char* | 84 | list_t *marks; // list of char* |
85 | 85 | ||
86 | float alpha; | ||
87 | |||
86 | struct { | 88 | struct { |
87 | struct wl_signal destroy; | 89 | struct wl_signal destroy; |
88 | // Raised after the tree updates, but before arrange_windows | 90 | // Raised after the tree updates, but before arrange_windows |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 4b84205e..f32ccc5a 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -59,11 +59,9 @@ struct sway_wl_shell_surface { | |||
59 | }; | 59 | }; |
60 | 60 | ||
61 | enum sway_view_type { | 61 | enum sway_view_type { |
62 | SWAY_WL_SHELL_VIEW, | 62 | SWAY_VIEW_WL_SHELL, |
63 | SWAY_XDG_SHELL_V6_VIEW, | 63 | SWAY_VIEW_XDG_SHELL_V6, |
64 | SWAY_XWAYLAND_VIEW, | 64 | SWAY_VIEW_XWAYLAND, |
65 | // Keep last | ||
66 | SWAY_VIEW_TYPES, | ||
67 | }; | 65 | }; |
68 | 66 | ||
69 | enum sway_view_prop { | 67 | enum sway_view_prop { |
@@ -101,9 +99,6 @@ struct sway_view { | |||
101 | struct sway_xwayland_surface *sway_xwayland_surface; | 99 | struct sway_xwayland_surface *sway_xwayland_surface; |
102 | struct sway_wl_shell_surface *sway_wl_shell_surface; | 100 | struct sway_wl_shell_surface *sway_wl_shell_surface; |
103 | }; | 101 | }; |
104 | |||
105 | // only used for unmanaged views (shell specific) | ||
106 | struct wl_list unmanaged_view_link; // sway_root::unmanaged_views | ||
107 | }; | 102 | }; |
108 | 103 | ||
109 | struct sway_view *view_create(enum sway_view_type type, | 104 | struct sway_view *view_create(enum sway_view_type type, |
diff --git a/include/swaylock/seat.h b/include/swaylock/seat.h new file mode 100644 index 00000000..44bc37d5 --- /dev/null +++ b/include/swaylock/seat.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef _SWAYLOCK_SEAT_H | ||
2 | #define _SWAYLOCK_SEAT_H | ||
3 | #include <xkbcommon/xkbcommon.h> | ||
4 | |||
5 | enum mod_bit { | ||
6 | MOD_SHIFT = 1<<0, | ||
7 | MOD_CAPS = 1<<1, | ||
8 | MOD_CTRL = 1<<2, | ||
9 | MOD_ALT = 1<<3, | ||
10 | MOD_MOD2 = 1<<4, | ||
11 | MOD_MOD3 = 1<<5, | ||
12 | MOD_LOGO = 1<<6, | ||
13 | MOD_MOD5 = 1<<7, | ||
14 | }; | ||
15 | |||
16 | enum mask { | ||
17 | MASK_SHIFT, | ||
18 | MASK_CAPS, | ||
19 | MASK_CTRL, | ||
20 | MASK_ALT, | ||
21 | MASK_MOD2, | ||
22 | MASK_MOD3, | ||
23 | MASK_LOGO, | ||
24 | MASK_MOD5, | ||
25 | MASK_LAST | ||
26 | }; | ||
27 | |||
28 | struct swaylock_xkb { | ||
29 | uint32_t modifiers; | ||
30 | struct xkb_state *state; | ||
31 | struct xkb_context *context; | ||
32 | struct xkb_keymap *keymap; | ||
33 | xkb_mod_mask_t masks[MASK_LAST]; | ||
34 | }; | ||
35 | |||
36 | extern const struct wl_seat_listener seat_listener; | ||
37 | |||
38 | #endif | ||
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index eeed094e..173e8b12 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h | |||
@@ -1,66 +1,64 @@ | |||
1 | #ifndef _SWAYLOCK_H | 1 | #ifndef _SWAYLOCK_H |
2 | #define _SWAYLOCK_H | 2 | #define _SWAYLOCK_H |
3 | 3 | #include <stdbool.h> | |
4 | #include "client/cairo.h" | 4 | #include <stdint.h> |
5 | 5 | #include <wayland-client.h> | |
6 | enum scaling_mode { | 6 | #include "background-image.h" |
7 | SCALING_MODE_STRETCH, | 7 | #include "cairo.h" |
8 | SCALING_MODE_FILL, | 8 | #include "pool-buffer.h" |
9 | SCALING_MODE_FIT, | 9 | #include "swaylock/seat.h" |
10 | SCALING_MODE_CENTER, | 10 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
11 | SCALING_MODE_TILE, | ||
12 | }; | ||
13 | 11 | ||
14 | enum auth_state { | 12 | enum auth_state { |
15 | AUTH_STATE_IDLE, | 13 | AUTH_STATE_IDLE, |
16 | AUTH_STATE_INPUT, | 14 | AUTH_STATE_INPUT, |
17 | AUTH_STATE_BACKSPACE, | 15 | AUTH_STATE_BACKSPACE, |
18 | AUTH_STATE_VALIDATING, | 16 | AUTH_STATE_VALIDATING, |
19 | AUTH_STATE_INVALID, | 17 | AUTH_STATE_INVALID, |
20 | }; | 18 | }; |
21 | 19 | ||
22 | enum line_source { | 20 | struct swaylock_args { |
23 | LINE_SOURCE_DEFAULT, | ||
24 | LINE_SOURCE_RING, | ||
25 | LINE_SOURCE_INSIDE, | ||
26 | }; | ||
27 | |||
28 | struct render_data { | ||
29 | list_t *surfaces; | ||
30 | // Output specific images | ||
31 | cairo_surface_t **images; | ||
32 | // OR one image for all outputs: | ||
33 | cairo_surface_t *image; | ||
34 | int num_images; | ||
35 | int color_set; | ||
36 | uint32_t color; | 21 | uint32_t color; |
37 | enum scaling_mode scaling_mode; | 22 | enum background_mode mode; |
38 | enum auth_state auth_state; | 23 | bool show_indicator; |
39 | }; | 24 | }; |
40 | 25 | ||
41 | struct lock_colors { | 26 | struct swaylock_password { |
42 | uint32_t inner_ring; | 27 | size_t size; |
43 | uint32_t outer_ring; | 28 | size_t len; |
29 | char *buffer; | ||
44 | }; | 30 | }; |
45 | 31 | ||
46 | struct lock_config { | 32 | struct swaylock_state { |
47 | char *font; | 33 | struct wl_display *display; |
48 | 34 | struct wl_compositor *compositor; | |
49 | struct { | 35 | struct zwlr_layer_shell_v1 *layer_shell; |
50 | uint32_t text; | 36 | struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; |
51 | uint32_t line; | 37 | struct wl_shm *shm; |
52 | uint32_t separator; | 38 | struct wl_list surfaces; |
53 | uint32_t input_cursor; | 39 | struct swaylock_args args; |
54 | uint32_t backspace_cursor; | 40 | struct swaylock_password password; |
55 | struct lock_colors normal; | 41 | struct swaylock_xkb xkb; |
56 | struct lock_colors validating; | 42 | enum auth_state auth_state; |
57 | struct lock_colors invalid; | 43 | bool run_display; |
58 | } colors; | 44 | }; |
59 | 45 | ||
60 | int radius; | 46 | struct swaylock_surface { |
61 | int thickness; | 47 | cairo_surface_t *image; |
48 | struct swaylock_state *state; | ||
49 | struct wl_output *output; | ||
50 | struct wl_surface *surface; | ||
51 | struct zwlr_layer_surface_v1 *layer_surface; | ||
52 | struct pool_buffer buffers[2]; | ||
53 | struct pool_buffer *current_buffer; | ||
54 | uint32_t width, height; | ||
55 | int32_t scale; | ||
56 | struct wl_list link; | ||
62 | }; | 57 | }; |
63 | 58 | ||
64 | void render(struct render_data* render_data, struct lock_config *config); | 59 | void swaylock_handle_key(struct swaylock_state *state, |
60 | xkb_keysym_t keysym, uint32_t codepoint); | ||
61 | void render_frame(struct swaylock_surface *surface); | ||
62 | void render_frames(struct swaylock_state *state); | ||
65 | 63 | ||
66 | #endif | 64 | #endif |
diff --git a/include/unicode.h b/include/unicode.h new file mode 100644 index 00000000..e2ee9588 --- /dev/null +++ b/include/unicode.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef _SWAY_UNICODE_H | ||
2 | #define _SWAY_UNICODE_H | ||
3 | #include <stddef.h> | ||
4 | #include <stdint.h> | ||
5 | |||
6 | // Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself | ||
7 | // doesn't really bother with more than 4. | ||
8 | #define UTF8_MAX_SIZE 4 | ||
9 | |||
10 | #define UTF8_INVALID 0x80 | ||
11 | |||
12 | /** | ||
13 | * Grabs the next UTF-8 character and advances the string pointer | ||
14 | */ | ||
15 | uint32_t utf8_decode(const char **str); | ||
16 | |||
17 | /** | ||
18 | * Encodes a character as UTF-8 and returns the length of that character. | ||
19 | */ | ||
20 | size_t utf8_encode(char *str, uint32_t ch); | ||
21 | |||
22 | /** | ||
23 | * Returns the size of the next UTF-8 character | ||
24 | */ | ||
25 | int utf8_size(const char *str); | ||
26 | |||
27 | /** | ||
28 | * Returns the size of a UTF-8 character | ||
29 | */ | ||
30 | size_t utf8_chsize(uint32_t ch); | ||
31 | |||
32 | #endif | ||
33 | |||
diff --git a/meson.build b/meson.build index 01788fd9..d02a446b 100644 --- a/meson.build +++ b/meson.build | |||
@@ -35,6 +35,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false) | |||
35 | pixman = dependency('pixman-1') | 35 | pixman = dependency('pixman-1') |
36 | libcap = dependency('libcap') | 36 | libcap = dependency('libcap') |
37 | libinput = dependency('libinput') | 37 | libinput = dependency('libinput') |
38 | libpam = cc.find_library('libpam') | ||
38 | math = cc.find_library('m') | 39 | math = cc.find_library('m') |
39 | rt = cc.find_library('rt') | 40 | rt = cc.find_library('rt') |
40 | git = find_program('git', required: false) | 41 | git = find_program('git', required: false) |
@@ -105,6 +106,7 @@ subdir('swaymsg') | |||
105 | subdir('client') | 106 | subdir('client') |
106 | subdir('swaybg') | 107 | subdir('swaybg') |
107 | subdir('swaybar') | 108 | subdir('swaybar') |
109 | subdir('swaylock') | ||
108 | 110 | ||
109 | config = configuration_data() | 111 | config = configuration_data() |
110 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 112 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) |
diff --git a/protocols/meson.build b/protocols/meson.build index 0887cf86..7f83b16b 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -22,12 +22,14 @@ wayland_scanner_server = generator( | |||
22 | 22 | ||
23 | client_protocols = [ | 23 | client_protocols = [ |
24 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | 24 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], |
25 | ['wlr-layer-shell-unstable-v1.xml'] | 25 | ['wlr-layer-shell-unstable-v1.xml'], |
26 | ['wlr-input-inhibitor-unstable-v1.xml'] | ||
26 | ] | 27 | ] |
27 | 28 | ||
28 | server_protocols = [ | 29 | server_protocols = [ |
29 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], | 30 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], |
30 | ['wlr-layer-shell-unstable-v1.xml'] | 31 | ['wlr-layer-shell-unstable-v1.xml'], |
32 | ['wlr-input-inhibitor-unstable-v1.xml'] | ||
31 | ] | 33 | ] |
32 | 34 | ||
33 | client_protos_src = [] | 35 | client_protos_src = [] |
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 00000000..b62d1bb4 --- /dev/null +++ b/protocols/wlr-input-inhibitor-unstable-v1.xml | |||
@@ -0,0 +1,67 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <protocol name="wlr_input_inhibit_unstable_v1"> | ||
3 | <copyright> | ||
4 | Copyright © 2018 Drew DeVault | ||
5 | |||
6 | Permission to use, copy, modify, distribute, and sell this | ||
7 | software and its documentation for any purpose is hereby granted | ||
8 | without fee, provided that the above copyright notice appear in | ||
9 | all copies and that both that copyright notice and this permission | ||
10 | notice appear in supporting documentation, and that the name of | ||
11 | the copyright holders not be used in advertising or publicity | ||
12 | pertaining to distribution of the software without specific, | ||
13 | written prior permission. The copyright holders make no | ||
14 | representations about the suitability of this software for any | ||
15 | purpose. It is provided "as is" without express or implied | ||
16 | warranty. | ||
17 | |||
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | ||
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | ||
25 | THIS SOFTWARE. | ||
26 | </copyright> | ||
27 | |||
28 | <interface name="zwlr_input_inhibit_manager_v1" version="1"> | ||
29 | <description summary="inhibits input events to other clients"> | ||
30 | Clients can use this interface to prevent input events from being sent to | ||
31 | any surfaces but its own, which is useful for example in lock screen | ||
32 | software. It is assumed that access to this interface will be locked down | ||
33 | to whitelisted clients by the compositor. | ||
34 | </description> | ||
35 | |||
36 | <request name="get_inhibitor"> | ||
37 | <description summary="inhibit input to other clients"> | ||
38 | Activates the input inhibitor. As long as the inhibitor is active, the | ||
39 | compositor will not send input events to other clients. | ||
40 | </description> | ||
41 | <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/> | ||
42 | </request> | ||
43 | |||
44 | <enum name="error"> | ||
45 | <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/> | ||
46 | </enum> | ||
47 | </interface> | ||
48 | |||
49 | <interface name="zwlr_input_inhibitor_v1" version="1"> | ||
50 | <description summary="inhibits input to other clients"> | ||
51 | While this resource exists, input to clients other than the owner of the | ||
52 | inhibitor resource will not receive input events. The client that owns | ||
53 | this resource will receive all input events normally. The compositor will | ||
54 | also disable all of its own input processing (such as keyboard shortcuts) | ||
55 | while the inhibitor is active. | ||
56 | |||
57 | The compositor may continue to send input events to selected clients, | ||
58 | such as an on-screen keyboard (via the input-method protocol). | ||
59 | </description> | ||
60 | |||
61 | <request name="destroy" type="destructor"> | ||
62 | <description summary="destroy the input inhibitor object"> | ||
63 | Destroy the inhibitor and allow other clients to receive input. | ||
64 | </description> | ||
65 | </request> | ||
66 | </interface> | ||
67 | </protocol> | ||
diff --git a/sway/commands.c b/sway/commands.c index 8156a08e..2786a879 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -163,6 +163,7 @@ static struct cmd_handler command_handlers[] = { | |||
163 | { "kill", cmd_kill }, | 163 | { "kill", cmd_kill }, |
164 | { "layout", cmd_layout }, | 164 | { "layout", cmd_layout }, |
165 | { "move", cmd_move }, | 165 | { "move", cmd_move }, |
166 | { "opacity", cmd_opacity }, | ||
166 | { "reload", cmd_reload }, | 167 | { "reload", cmd_reload }, |
167 | { "split", cmd_split }, | 168 | { "split", cmd_split }, |
168 | { "splith", cmd_splith }, | 169 | { "splith", cmd_splith }, |
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c new file mode 100644 index 00000000..b8cd1f09 --- /dev/null +++ b/sway/commands/opacity.c | |||
@@ -0,0 +1,39 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdlib.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/tree/view.h" | ||
5 | #include "log.h" | ||
6 | |||
7 | static bool parse_opacity(const char *opacity, float *val) { | ||
8 | char *err; | ||
9 | *val = strtof(opacity, &err); | ||
10 | if (*val < 0 || *val > 1 || *err) { | ||
11 | return false; | ||
12 | } | ||
13 | return true; | ||
14 | } | ||
15 | |||
16 | struct cmd_results *cmd_opacity(int argc, char **argv) { | ||
17 | struct cmd_results *error = NULL; | ||
18 | if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) { | ||
19 | return error; | ||
20 | } | ||
21 | |||
22 | struct sway_container *con = | ||
23 | config->handler_context.current_container; | ||
24 | |||
25 | float opacity = 0.0f; | ||
26 | |||
27 | if (!parse_opacity(argv[0], &opacity)) { | ||
28 | return cmd_results_new(CMD_INVALID, "opacity <value>", | ||
29 | "Invalid value (expected 0..1): %s", argv[0]); | ||
30 | } | ||
31 | |||
32 | con->alpha = opacity; | ||
33 | |||
34 | if (con->type == C_VIEW) { | ||
35 | view_damage_whole(con->sway_view); | ||
36 | } | ||
37 | |||
38 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
39 | } | ||
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8a4fb4a2..0e8a9485 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -75,7 +75,7 @@ static bool surface_intersect_output(struct wlr_surface *surface, | |||
75 | 75 | ||
76 | static void render_surface(struct wlr_surface *surface, | 76 | static void render_surface(struct wlr_surface *surface, |
77 | struct wlr_output *wlr_output, struct timespec *when, | 77 | struct wlr_output *wlr_output, struct timespec *when, |
78 | double ox, double oy, float rotation) { | 78 | double ox, double oy, float rotation, float alpha) { |
79 | struct wlr_renderer *renderer = | 79 | struct wlr_renderer *renderer = |
80 | wlr_backend_get_renderer(wlr_output->backend); | 80 | wlr_backend_get_renderer(wlr_output->backend); |
81 | 81 | ||
@@ -95,8 +95,8 @@ static void render_surface(struct wlr_surface *surface, | |||
95 | wlr_matrix_project_box(matrix, &box, transform, rotation, | 95 | wlr_matrix_project_box(matrix, &box, transform, rotation, |
96 | wlr_output->transform_matrix); | 96 | wlr_output->transform_matrix); |
97 | 97 | ||
98 | // TODO: configurable alpha | 98 | wlr_render_texture_with_matrix(renderer, surface->texture, |
99 | wlr_render_texture_with_matrix(renderer, surface->texture, matrix, 1.0f); | 99 | matrix, alpha); |
100 | 100 | ||
101 | wlr_surface_send_frame_done(surface, when); | 101 | wlr_surface_send_frame_done(surface, when); |
102 | } | 102 | } |
@@ -110,13 +110,13 @@ static void render_surface(struct wlr_surface *surface, | |||
110 | surface->current->width, surface->current->height, rotation); | 110 | surface->current->width, surface->current->height, rotation); |
111 | 111 | ||
112 | render_surface(subsurface->surface, wlr_output, when, | 112 | render_surface(subsurface->surface, wlr_output, when, |
113 | ox + sx, oy + sy, rotation); | 113 | ox + sx, oy + sy, rotation, alpha); |
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, | 117 | static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, |
118 | struct wlr_output *wlr_output, struct timespec *when, double base_x, | 118 | struct wlr_output *wlr_output, struct timespec *when, double base_x, |
119 | double base_y, float rotation) { | 119 | double base_y, float rotation, float alpha) { |
120 | double width = surface->surface->current->width; | 120 | double width = surface->surface->current->width; |
121 | double height = surface->surface->current->height; | 121 | double height = surface->surface->current->height; |
122 | 122 | ||
@@ -136,19 +136,19 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, | |||
136 | width, height, rotation); | 136 | width, height, rotation); |
137 | 137 | ||
138 | render_surface(popup->surface, wlr_output, when, | 138 | render_surface(popup->surface, wlr_output, when, |
139 | base_x + popup_sx, base_y + popup_sy, rotation); | 139 | base_x + popup_sx, base_y + popup_sy, rotation, alpha); |
140 | render_xdg_v6_popups(popup, wlr_output, when, | 140 | render_xdg_v6_popups(popup, wlr_output, when, |
141 | base_x + popup_sx, base_y + popup_sy, rotation); | 141 | base_x + popup_sx, base_y + popup_sy, rotation, alpha); |
142 | } | 142 | } |
143 | } | 143 | } |
144 | 144 | ||
145 | static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, | 145 | static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, |
146 | struct wlr_output *wlr_output, struct timespec *when, | 146 | struct wlr_output *wlr_output, struct timespec *when, |
147 | double lx, double ly, float rotation, | 147 | double lx, double ly, float rotation, float alpha, |
148 | bool is_child) { | 148 | bool is_child) { |
149 | if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { | 149 | if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { |
150 | render_surface(surface->surface, wlr_output, when, | 150 | render_surface(surface->surface, wlr_output, when, |
151 | lx, ly, rotation); | 151 | lx, ly, rotation, alpha); |
152 | 152 | ||
153 | double width = surface->surface->current->width; | 153 | double width = surface->surface->current->width; |
154 | double height = surface->surface->current->height; | 154 | double height = surface->surface->current->height; |
@@ -164,7 +164,7 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, | |||
164 | width, height, rotation); | 164 | width, height, rotation); |
165 | 165 | ||
166 | render_wl_shell_surface(popup, wlr_output, when, | 166 | render_wl_shell_surface(popup, wlr_output, when, |
167 | lx + popup_x, ly + popup_y, rotation, true); | 167 | lx + popup_x, ly + popup_y, rotation, alpha, true); |
168 | } | 168 | } |
169 | } | 169 | } |
170 | } | 170 | } |
@@ -181,29 +181,28 @@ static void render_view(struct sway_container *view, void *data) { | |||
181 | struct wlr_output *wlr_output = output->wlr_output; | 181 | struct wlr_output *wlr_output = output->wlr_output; |
182 | struct sway_view *sway_view = view->sway_view; | 182 | struct sway_view *sway_view = view->sway_view; |
183 | struct wlr_surface *surface = sway_view->surface; | 183 | struct wlr_surface *surface = sway_view->surface; |
184 | float alpha = sway_view->swayc->alpha; | ||
184 | 185 | ||
185 | if (!surface) { | 186 | if (!surface) { |
186 | return; | 187 | return; |
187 | } | 188 | } |
188 | 189 | ||
189 | switch (sway_view->type) { | 190 | switch (sway_view->type) { |
190 | case SWAY_XDG_SHELL_V6_VIEW: { | 191 | case SWAY_VIEW_XDG_SHELL_V6: { |
191 | int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x; | 192 | int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x; |
192 | int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y; | 193 | int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y; |
193 | render_surface(surface, wlr_output, when, | 194 | render_surface(surface, wlr_output, when, |
194 | view->x - window_offset_x, view->y - window_offset_y, 0); | 195 | view->x - window_offset_x, view->y - window_offset_y, 0, alpha); |
195 | render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output, | 196 | render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output, |
196 | when, view->x - window_offset_x, view->y - window_offset_y, 0); | 197 | when, view->x - window_offset_x, view->y - window_offset_y, 0, alpha); |
197 | break; | 198 | break; |
198 | } | 199 | } |
199 | case SWAY_WL_SHELL_VIEW: | 200 | case SWAY_VIEW_WL_SHELL: |
200 | render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output, | 201 | render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output, |
201 | when, view->x, view->y, 0, false); | 202 | when, view->x, view->y, 0, alpha, false); |
202 | break; | 203 | break; |
203 | case SWAY_XWAYLAND_VIEW: | 204 | case SWAY_VIEW_XWAYLAND: |
204 | render_surface(surface, wlr_output, when, view->x, view->y, 0); | 205 | render_surface(surface, wlr_output, when, view->x, view->y, 0, alpha); |
205 | break; | ||
206 | default: | ||
207 | break; | 206 | break; |
208 | } | 207 | } |
209 | } | 208 | } |
@@ -214,7 +213,7 @@ static void render_layer(struct sway_output *output, struct timespec *when, | |||
214 | wl_list_for_each(sway_layer, layer, link) { | 213 | wl_list_for_each(sway_layer, layer, link) { |
215 | struct wlr_layer_surface *layer = sway_layer->layer_surface; | 214 | struct wlr_layer_surface *layer = sway_layer->layer_surface; |
216 | render_surface(layer->surface, output->wlr_output, when, | 215 | render_surface(layer->surface, output->wlr_output, when, |
217 | sway_layer->geo.x, sway_layer->geo.y, 0); | 216 | sway_layer->geo.x, sway_layer->geo.y, 0, 1.0f); |
218 | wlr_surface_send_frame_done(layer->surface, when); | 217 | wlr_surface_send_frame_done(layer->surface, when); |
219 | } | 218 | } |
220 | } | 219 | } |
@@ -288,7 +287,7 @@ static void render_output(struct sway_output *output, struct timespec *when, | |||
288 | } | 287 | } |
289 | 288 | ||
290 | render_surface(xsurface->surface, wlr_output, &output->last_frame, | 289 | render_surface(xsurface->surface, wlr_output, &output->last_frame, |
291 | view_box.x - output_box->x, view_box.y - output_box->y, 0); | 290 | view_box.x - output_box->x, view_box.y - output_box->y, 0, 1.0f); |
292 | } | 291 | } |
293 | 292 | ||
294 | // TODO: Consider revising this when fullscreen windows are supported | 293 | // TODO: Consider revising this when fullscreen windows are supported |
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c index 6528a397..a470674d 100644 --- a/sway/desktop/wl_shell.c +++ b/sway/desktop/wl_shell.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include "log.h" | 12 | #include "log.h" |
13 | 13 | ||
14 | static bool assert_wl_shell(struct sway_view *view) { | 14 | static bool assert_wl_shell(struct sway_view *view) { |
15 | return sway_assert(view->type == SWAY_WL_SHELL_VIEW, | 15 | return sway_assert(view->type == SWAY_VIEW_WL_SHELL, |
16 | "Expecting wl_shell view!"); | 16 | "Expecting wl_shell view!"); |
17 | } | 17 | } |
18 | 18 | ||
@@ -97,7 +97,7 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { | |||
97 | return; | 97 | return; |
98 | } | 98 | } |
99 | 99 | ||
100 | struct sway_view *view = view_create(SWAY_WL_SHELL_VIEW, &view_impl); | 100 | struct sway_view *view = view_create(SWAY_VIEW_WL_SHELL, &view_impl); |
101 | if (!sway_assert(view, "Failed to allocate view")) { | 101 | if (!sway_assert(view, "Failed to allocate view")) { |
102 | return; | 102 | return; |
103 | } | 103 | } |
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 49305b39..5cdb8f9f 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include "log.h" | 12 | #include "log.h" |
13 | 13 | ||
14 | static bool assert_xdg(struct sway_view *view) { | 14 | static bool assert_xdg(struct sway_view *view) { |
15 | return sway_assert(view->type == SWAY_XDG_SHELL_V6_VIEW, | 15 | return sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6, |
16 | "Expected xdg shell v6 view!"); | 16 | "Expected xdg shell v6 view!"); |
17 | } | 17 | } |
18 | 18 | ||
@@ -126,7 +126,7 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
126 | return; | 126 | return; |
127 | } | 127 | } |
128 | 128 | ||
129 | struct sway_view *view = view_create(SWAY_XDG_SHELL_V6_VIEW, &view_impl); | 129 | struct sway_view *view = view_create(SWAY_VIEW_XDG_SHELL_V6, &view_impl); |
130 | if (!sway_assert(view, "Failed to allocate view")) { | 130 | if (!sway_assert(view, "Failed to allocate view")) { |
131 | return; | 131 | return; |
132 | } | 132 | } |
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index bfef68cf..a793928c 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -42,7 +42,7 @@ static void create_unmanaged(struct wlr_xwayland_surface *xsurface) { | |||
42 | 42 | ||
43 | 43 | ||
44 | static bool assert_xwayland(struct sway_view *view) { | 44 | static bool assert_xwayland(struct sway_view *view) { |
45 | return sway_assert(view->type == SWAY_XWAYLAND_VIEW, | 45 | return sway_assert(view->type == SWAY_VIEW_XWAYLAND, |
46 | "Expected xwayland view!"); | 46 | "Expected xwayland view!"); |
47 | } | 47 | } |
48 | 48 | ||
@@ -185,7 +185,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
185 | return; | 185 | return; |
186 | } | 186 | } |
187 | 187 | ||
188 | struct sway_view *view = view_create(SWAY_XWAYLAND_VIEW, &view_impl); | 188 | struct sway_view *view = view_create(SWAY_VIEW_XWAYLAND, &view_impl); |
189 | if (!sway_assert(view, "Failed to allocate view")) { | 189 | if (!sway_assert(view, "Failed to allocate view")) { |
190 | return; | 190 | return; |
191 | } | 191 | } |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9229e92d..195ddce9 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -146,8 +146,10 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor, | |||
146 | 146 | ||
147 | // send pointer enter/leave | 147 | // send pointer enter/leave |
148 | if (surface != NULL) { | 148 | if (surface != NULL) { |
149 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); | 149 | if (seat_is_input_allowed(cursor->seat, surface)) { |
150 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | 150 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); |
151 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | ||
152 | } | ||
151 | } else { | 153 | } else { |
152 | wlr_seat_pointer_clear_focus(seat); | 154 | wlr_seat_pointer_clear_focus(seat); |
153 | } | 155 | } |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index c3507f65..f71a06e4 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <libinput.h> | 7 | #include <libinput.h> |
8 | #include <math.h> | 8 | #include <math.h> |
9 | #include <wlr/backend/libinput.h> | 9 | #include <wlr/backend/libinput.h> |
10 | #include <wlr/types/wlr_input_inhibitor.h> | ||
10 | #include "sway/config.h" | 11 | #include "sway/config.h" |
11 | #include "sway/input/input-manager.h" | 12 | #include "sway/input/input-manager.h" |
12 | #include "sway/input/seat.h" | 13 | #include "sway/input/seat.h" |
@@ -263,6 +264,32 @@ static void handle_new_input(struct wl_listener *listener, void *data) { | |||
263 | input_device->device_destroy.notify = handle_device_destroy; | 264 | input_device->device_destroy.notify = handle_device_destroy; |
264 | } | 265 | } |
265 | 266 | ||
267 | static void handle_inhibit_activate(struct wl_listener *listener, void *data) { | ||
268 | struct sway_input_manager *input_manager = wl_container_of( | ||
269 | listener, input_manager, inhibit_activate); | ||
270 | struct sway_seat *seat; | ||
271 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
272 | seat_set_exclusive_client(seat, input_manager->inhibit->active_client); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) { | ||
277 | struct sway_input_manager *input_manager = wl_container_of( | ||
278 | listener, input_manager, inhibit_deactivate); | ||
279 | struct sway_seat *seat; | ||
280 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
281 | seat_set_exclusive_client(seat, NULL); | ||
282 | struct sway_container *previous = seat_get_focus(seat); | ||
283 | if (previous) { | ||
284 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | ||
285 | container_type_to_str(previous->type), previous->name); | ||
286 | // Hack to get seat to re-focus the return value of get_focus | ||
287 | seat_set_focus(seat, previous->parent); | ||
288 | seat_set_focus(seat, previous); | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
266 | struct sway_input_manager *input_manager_create( | 293 | struct sway_input_manager *input_manager_create( |
267 | struct sway_server *server) { | 294 | struct sway_server *server) { |
268 | struct sway_input_manager *input = | 295 | struct sway_input_manager *input = |
@@ -281,6 +308,14 @@ struct sway_input_manager *input_manager_create( | |||
281 | input->new_input.notify = handle_new_input; | 308 | input->new_input.notify = handle_new_input; |
282 | wl_signal_add(&server->backend->events.new_input, &input->new_input); | 309 | wl_signal_add(&server->backend->events.new_input, &input->new_input); |
283 | 310 | ||
311 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | ||
312 | input->inhibit_activate.notify = handle_inhibit_activate; | ||
313 | wl_signal_add(&input->inhibit->events.activate, | ||
314 | &input->inhibit_activate); | ||
315 | input->inhibit_deactivate.notify = handle_inhibit_deactivate; | ||
316 | wl_signal_add(&input->inhibit->events.deactivate, | ||
317 | &input->inhibit_deactivate); | ||
318 | |||
284 | return input; | 319 | return input; |
285 | } | 320 | } |
286 | 321 | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index 50134aae..e3df6955 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <assert.h> | 3 | #include <assert.h> |
4 | #include <time.h> | ||
3 | #include <wlr/types/wlr_cursor.h> | 5 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
5 | #include <wlr/types/wlr_xcursor_manager.h> | 7 | #include <wlr/types/wlr_xcursor_manager.h> |
@@ -9,6 +11,7 @@ | |||
9 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
10 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
11 | #include "sway/ipc-server.h" | 13 | #include "sway/ipc-server.h" |
14 | #include "sway/layers.h" | ||
12 | #include "sway/output.h" | 15 | #include "sway/output.h" |
13 | #include "sway/tree/container.h" | 16 | #include "sway/tree/container.h" |
14 | #include "sway/tree/view.h" | 17 | #include "sway/tree/view.h" |
@@ -63,7 +66,7 @@ static void seat_send_focus(struct sway_seat *seat, | |||
63 | return; | 66 | return; |
64 | } | 67 | } |
65 | struct sway_view *view = con->sway_view; | 68 | struct sway_view *view = con->sway_view; |
66 | if (view->type == SWAY_XWAYLAND_VIEW) { | 69 | if (view->type == SWAY_VIEW_XWAYLAND) { |
67 | struct wlr_xwayland *xwayland = | 70 | struct wlr_xwayland *xwayland = |
68 | seat->input->server->xwayland; | 71 | seat->input->server->xwayland; |
69 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 72 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
@@ -350,6 +353,12 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
350 | seat->cursor->cursor->y); | 353 | seat->cursor->cursor->y); |
351 | } | 354 | } |
352 | 355 | ||
356 | bool seat_is_input_allowed(struct sway_seat *seat, | ||
357 | struct wlr_surface *surface) { | ||
358 | struct wl_client *client = wl_resource_get_client(surface->resource); | ||
359 | return !seat->exclusive_client || seat->exclusive_client == client; | ||
360 | } | ||
361 | |||
353 | void seat_set_focus_warp(struct sway_seat *seat, | 362 | void seat_set_focus_warp(struct sway_seat *seat, |
354 | struct sway_container *container, bool warp) { | 363 | struct sway_container *container, bool warp) { |
355 | if (seat->focused_layer) { | 364 | if (seat->focused_layer) { |
@@ -371,6 +380,12 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
371 | wl_list_remove(&seat_con->link); | 380 | wl_list_remove(&seat_con->link); |
372 | wl_list_insert(&seat->focus_stack, &seat_con->link); | 381 | wl_list_insert(&seat->focus_stack, &seat_con->link); |
373 | 382 | ||
383 | if (container->type == C_VIEW && !seat_is_input_allowed( | ||
384 | seat, container->sway_view->surface)) { | ||
385 | wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); | ||
386 | return; | ||
387 | } | ||
388 | |||
374 | if (container->type == C_VIEW) { | 389 | if (container->type == C_VIEW) { |
375 | seat_send_focus(seat, container); | 390 | seat_send_focus(seat, container); |
376 | } | 391 | } |
@@ -424,11 +439,18 @@ void seat_set_focus(struct sway_seat *seat, | |||
424 | 439 | ||
425 | void seat_set_focus_layer(struct sway_seat *seat, | 440 | void seat_set_focus_layer(struct sway_seat *seat, |
426 | struct wlr_layer_surface *layer) { | 441 | struct wlr_layer_surface *layer) { |
427 | if (!layer) { | 442 | if (!layer && seat->focused_layer) { |
428 | seat->focused_layer = NULL; | 443 | seat->focused_layer = NULL; |
444 | struct sway_container *previous = seat_get_focus(seat); | ||
445 | if (previous) { | ||
446 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | ||
447 | container_type_to_str(previous->type), previous->name); | ||
448 | // Hack to get seat to re-focus the return value of get_focus | ||
449 | seat_set_focus(seat, previous->parent); | ||
450 | seat_set_focus(seat, previous); | ||
451 | } | ||
429 | return; | 452 | return; |
430 | } | 453 | } else if (!layer || seat->focused_layer == layer) { |
431 | if (seat->focused_layer == layer) { | ||
432 | return; | 454 | return; |
433 | } | 455 | } |
434 | if (seat->has_focus) { | 456 | if (seat->has_focus) { |
@@ -453,6 +475,51 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
453 | } | 475 | } |
454 | } | 476 | } |
455 | 477 | ||
478 | void seat_set_exclusive_client(struct sway_seat *seat, | ||
479 | struct wl_client *client) { | ||
480 | if (!client) { | ||
481 | seat->exclusive_client = client; | ||
482 | // Triggers a refocus of the topmost surface layer if necessary | ||
483 | // TODO: Make layer surface focus per-output based on cursor position | ||
484 | for (int i = 0; i < root_container.children->length; ++i) { | ||
485 | struct sway_container *output = root_container.children->items[i]; | ||
486 | if (!sway_assert(output->type == C_OUTPUT, | ||
487 | "root container has non-output child")) { | ||
488 | continue; | ||
489 | } | ||
490 | arrange_layers(output->sway_output); | ||
491 | } | ||
492 | return; | ||
493 | } | ||
494 | if (seat->focused_layer) { | ||
495 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | ||
496 | seat_set_focus_layer(seat, NULL); | ||
497 | } | ||
498 | } | ||
499 | if (seat->has_focus) { | ||
500 | struct sway_container *focus = seat_get_focus(seat); | ||
501 | if (focus->type == C_VIEW && wl_resource_get_client( | ||
502 | focus->sway_view->surface->resource) != client) { | ||
503 | seat_set_focus(seat, NULL); | ||
504 | } | ||
505 | } | ||
506 | if (seat->wlr_seat->pointer_state.focused_client) { | ||
507 | if (seat->wlr_seat->pointer_state.focused_client->client != client) { | ||
508 | wlr_seat_pointer_clear_focus(seat->wlr_seat); | ||
509 | } | ||
510 | } | ||
511 | struct timespec now; | ||
512 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
513 | struct wlr_touch_point *point; | ||
514 | wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) { | ||
515 | if (point->client->client != client) { | ||
516 | wlr_seat_touch_point_clear_focus(seat->wlr_seat, | ||
517 | now.tv_nsec / 1000, point->touch_id); | ||
518 | } | ||
519 | } | ||
520 | seat->exclusive_client = client; | ||
521 | } | ||
522 | |||
456 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, | 523 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, |
457 | struct sway_container *container) { | 524 | struct sway_container *container) { |
458 | return seat_get_focus_by_type(seat, container, C_TYPES); | 525 | return seat_get_focus_by_type(seat, container, C_TYPES); |
diff --git a/sway/meson.build b/sway/meson.build index 91aab0a0..f210c195 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -15,6 +15,7 @@ sway_sources = files( | |||
15 | 'commands/focus.c', | 15 | 'commands/focus.c', |
16 | 'commands/focus_follows_mouse.c', | 16 | 'commands/focus_follows_mouse.c', |
17 | 'commands/kill.c', | 17 | 'commands/kill.c', |
18 | 'commands/opacity.c', | ||
18 | 'commands/include.c', | 19 | 'commands/include.c', |
19 | 'commands/input.c', | 20 | 'commands/input.c', |
20 | 'commands/layout.c', | 21 | 'commands/layout.c', |
diff --git a/sway/sway.5.txt b/sway/sway.5.txt index 900e499a..59c3295a 100644 --- a/sway/sway.5.txt +++ b/sway/sway.5.txt | |||
@@ -413,6 +413,10 @@ The default colors are: | |||
413 | However, any mark that starts with an underscore will not be drawn even if the | 413 | However, any mark that starts with an underscore will not be drawn even if the |
414 | option is on. The default option is _on_. | 414 | option is on. The default option is _on_. |
415 | 415 | ||
416 | **opacity** <value>:: | ||
417 | Set the opacity of the window between 0 (completely transparent) and 1 | ||
418 | (completely opaque). | ||
419 | |||
416 | **unmark** <identifier>:: | 420 | **unmark** <identifier>:: |
417 | **Unmark** will remove _identifier_ from the list of current marks on a window. If | 421 | **Unmark** will remove _identifier_ from the list of current marks on a window. If |
418 | no _identifier_ is specified, then **unmark** will remove all marks. | 422 | no _identifier_ is specified, then **unmark** will remove all marks. |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 92c00f83..8fc9e3e8 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -78,6 +78,8 @@ struct sway_container *container_create(enum sway_container_type type) { | |||
78 | c->layout = L_NONE; | 78 | c->layout = L_NONE; |
79 | c->workspace_layout = L_NONE; | 79 | c->workspace_layout = L_NONE; |
80 | c->type = type; | 80 | c->type = type; |
81 | c->alpha = 1.0f; | ||
82 | |||
81 | if (type != C_VIEW) { | 83 | if (type != C_VIEW) { |
82 | c->children = create_list(); | 84 | c->children = create_list(); |
83 | } | 85 | } |
@@ -416,51 +418,33 @@ struct sway_container *container_at(struct sway_container *parent, | |||
416 | double view_sx = ox - swayc->x; | 418 | double view_sx = ox - swayc->x; |
417 | double view_sy = oy - swayc->y; | 419 | double view_sy = oy - swayc->y; |
418 | 420 | ||
421 | double _sx, _sy; | ||
422 | struct wlr_surface *_surface; | ||
419 | switch (sview->type) { | 423 | switch (sview->type) { |
420 | case SWAY_WL_SHELL_VIEW: | 424 | case SWAY_VIEW_XWAYLAND: |
421 | break; | 425 | _surface = wlr_surface_surface_at(sview->surface, |
422 | case SWAY_XDG_SHELL_V6_VIEW: | 426 | view_sx, view_sy, &_sx, &_sy); |
423 | // the top left corner of the sway container is the | 427 | break; |
424 | // coordinate of the top left corner of the window geometry | 428 | case SWAY_VIEW_WL_SHELL: |
425 | view_sx += sview->wlr_xdg_surface_v6->geometry.x; | 429 | _surface = wlr_wl_shell_surface_surface_at( |
426 | view_sy += sview->wlr_xdg_surface_v6->geometry.y; | 430 | sview->wlr_wl_shell_surface, |
427 | 431 | view_sx, view_sy, &_sx, &_sy); | |
428 | // check for popups | 432 | break; |
429 | double popup_sx, popup_sy; | 433 | case SWAY_VIEW_XDG_SHELL_V6: |
430 | struct wlr_xdg_surface_v6 *popup = | 434 | // the top left corner of the sway container is the |
431 | wlr_xdg_surface_v6_popup_at(sview->wlr_xdg_surface_v6, | 435 | // coordinate of the top left corner of the window geometry |
432 | view_sx, view_sy, &popup_sx, &popup_sy); | 436 | view_sx += sview->wlr_xdg_surface_v6->geometry.x; |
433 | 437 | view_sy += sview->wlr_xdg_surface_v6->geometry.y; | |
434 | if (popup) { | 438 | |
435 | *sx = view_sx - popup_sx; | 439 | _surface = wlr_xdg_surface_v6_surface_at( |
436 | *sy = view_sy - popup_sy; | 440 | sview->wlr_xdg_surface_v6, |
437 | *surface = popup->surface; | 441 | view_sx, view_sy, &_sx, &_sy); |
438 | return swayc; | 442 | break; |
439 | } | ||
440 | break; | ||
441 | case SWAY_XWAYLAND_VIEW: | ||
442 | break; | ||
443 | default: | ||
444 | break; | ||
445 | } | ||
446 | |||
447 | // check for subsurfaces | ||
448 | double sub_x, sub_y; | ||
449 | struct wlr_subsurface *subsurface = | ||
450 | wlr_surface_subsurface_at(sview->surface, | ||
451 | view_sx, view_sy, &sub_x, &sub_y); | ||
452 | if (subsurface) { | ||
453 | *sx = view_sx - sub_x; | ||
454 | *sy = view_sy - sub_y; | ||
455 | *surface = subsurface->surface; | ||
456 | return swayc; | ||
457 | } | 443 | } |
458 | 444 | if (_surface) { | |
459 | if (wlr_surface_point_accepts_input( | 445 | *sx = _sx; |
460 | sview->surface, view_sx, view_sy)) { | 446 | *sy = _sy; |
461 | *sx = view_sx; | 447 | *surface = _surface; |
462 | *sy = view_sy; | ||
463 | *surface = swayc->sway_view->surface; | ||
464 | return swayc; | 448 | return swayc; |
465 | } | 449 | } |
466 | } else { | 450 | } else { |
diff --git a/swaybg/main.c b/swaybg/main.c index c282a707..679b8c20 100644 --- a/swaybg/main.c +++ b/swaybg/main.c | |||
@@ -7,20 +7,12 @@ | |||
7 | #include <time.h> | 7 | #include <time.h> |
8 | #include <wayland-client.h> | 8 | #include <wayland-client.h> |
9 | #include <wlr/util/log.h> | 9 | #include <wlr/util/log.h> |
10 | #include "background-image.h" | ||
10 | #include "pool-buffer.h" | 11 | #include "pool-buffer.h" |
11 | #include "cairo.h" | 12 | #include "cairo.h" |
12 | #include "util.h" | 13 | #include "util.h" |
13 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 14 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
14 | 15 | ||
15 | enum background_mode { | ||
16 | BACKGROUND_MODE_STRETCH, | ||
17 | BACKGROUND_MODE_FILL, | ||
18 | BACKGROUND_MODE_FIT, | ||
19 | BACKGROUND_MODE_CENTER, | ||
20 | BACKGROUND_MODE_TILE, | ||
21 | BACKGROUND_MODE_SOLID_COLOR, | ||
22 | }; | ||
23 | |||
24 | struct swaybg_args { | 16 | struct swaybg_args { |
25 | int output_idx; | 17 | int output_idx; |
26 | const char *path; | 18 | const char *path; |
@@ -71,85 +63,18 @@ bool is_valid_color(const char *color) { | |||
71 | return true; | 63 | return true; |
72 | } | 64 | } |
73 | 65 | ||
74 | static void render_image(struct swaybg_state *state) { | ||
75 | cairo_t *cairo = state->current_buffer->cairo; | ||
76 | cairo_surface_t *image = state->context.image; | ||
77 | double width = cairo_image_surface_get_width(image); | ||
78 | double height = cairo_image_surface_get_height(image); | ||
79 | int buffer_width = state->width * state->scale; | ||
80 | int buffer_height = state->height * state->scale; | ||
81 | |||
82 | switch (state->args->mode) { | ||
83 | case BACKGROUND_MODE_STRETCH: | ||
84 | cairo_scale(cairo, (double)buffer_width / width, | ||
85 | (double)buffer_height / height); | ||
86 | cairo_set_source_surface(cairo, image, 0, 0); | ||
87 | break; | ||
88 | case BACKGROUND_MODE_FILL: { | ||
89 | double window_ratio = (double)buffer_width / buffer_height; | ||
90 | double bg_ratio = width / height; | ||
91 | |||
92 | if (window_ratio > bg_ratio) { | ||
93 | double scale = (double)buffer_width / width; | ||
94 | cairo_scale(cairo, scale, scale); | ||
95 | cairo_set_source_surface(cairo, image, | ||
96 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
97 | } else { | ||
98 | double scale = (double)buffer_height / height; | ||
99 | cairo_scale(cairo, scale, scale); | ||
100 | cairo_set_source_surface(cairo, image, | ||
101 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
102 | } | ||
103 | break; | ||
104 | } | ||
105 | case BACKGROUND_MODE_FIT: { | ||
106 | double window_ratio = (double)buffer_width / buffer_height; | ||
107 | double bg_ratio = width / height; | ||
108 | |||
109 | if (window_ratio > bg_ratio) { | ||
110 | double scale = (double)buffer_height / height; | ||
111 | cairo_scale(cairo, scale, scale); | ||
112 | cairo_set_source_surface(cairo, image, | ||
113 | (double)buffer_width / 2 / scale - width / 2, 0); | ||
114 | } else { | ||
115 | double scale = (double)buffer_width / width; | ||
116 | cairo_scale(cairo, scale, scale); | ||
117 | cairo_set_source_surface(cairo, image, | ||
118 | 0, (double)buffer_height / 2 / scale - height / 2); | ||
119 | } | ||
120 | break; | ||
121 | } | ||
122 | case BACKGROUND_MODE_CENTER: | ||
123 | cairo_set_source_surface(cairo, image, | ||
124 | (double)buffer_width / 2 - width / 2, | ||
125 | (double)buffer_height / 2 - height / 2); | ||
126 | break; | ||
127 | case BACKGROUND_MODE_TILE: { | ||
128 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | ||
129 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); | ||
130 | cairo_set_source(cairo, pattern); | ||
131 | break; | ||
132 | } | ||
133 | case BACKGROUND_MODE_SOLID_COLOR: | ||
134 | assert(0); | ||
135 | break; | ||
136 | } | ||
137 | cairo_paint(cairo); | ||
138 | } | ||
139 | |||
140 | static void render_frame(struct swaybg_state *state) { | 66 | static void render_frame(struct swaybg_state *state) { |
141 | state->current_buffer = get_next_buffer(state->shm, state->buffers, | 67 | int buffer_width = state->width * state->scale, |
142 | state->width * state->scale, state->height * state->scale); | 68 | buffer_height = state->height * state->scale; |
69 | state->current_buffer = get_next_buffer(state->shm, | ||
70 | state->buffers, buffer_width, buffer_height); | ||
143 | cairo_t *cairo = state->current_buffer->cairo; | 71 | cairo_t *cairo = state->current_buffer->cairo; |
144 | 72 | if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { | |
145 | switch (state->args->mode) { | ||
146 | case BACKGROUND_MODE_SOLID_COLOR: | ||
147 | cairo_set_source_u32(cairo, state->context.color); | 73 | cairo_set_source_u32(cairo, state->context.color); |
148 | cairo_paint(cairo); | 74 | cairo_paint(cairo); |
149 | break; | 75 | } else { |
150 | default: | 76 | render_background_image(cairo, state->context.image, |
151 | render_image(state); | 77 | state->args->mode, buffer_width, buffer_height); |
152 | break; | ||
153 | } | 78 | } |
154 | 79 | ||
155 | wl_surface_set_buffer_scale(state->surface, state->scale); | 80 | wl_surface_set_buffer_scale(state->surface, state->scale); |
@@ -163,31 +88,7 @@ static bool prepare_context(struct swaybg_state *state) { | |||
163 | state->context.color = parse_color(state->args->path); | 88 | state->context.color = parse_color(state->args->path); |
164 | return is_valid_color(state->args->path); | 89 | return is_valid_color(state->args->path); |
165 | } | 90 | } |
166 | #ifdef HAVE_GDK_PIXBUF | 91 | if (!(state->context.image = load_background_image(state->args->path))) { |
167 | GError *err = NULL; | ||
168 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err); | ||
169 | if (!pixbuf) { | ||
170 | wlr_log(L_ERROR, "Failed to load background image."); | ||
171 | return false; | ||
172 | } | ||
173 | state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); | ||
174 | g_object_unref(pixbuf); | ||
175 | #else | ||
176 | state->context.image = cairo_image_surface_create_from_png( | ||
177 | state->args->path); | ||
178 | #endif //HAVE_GDK_PIXBUF | ||
179 | if (!state->context.image) { | ||
180 | wlr_log(L_ERROR, "Failed to read background image."); | ||
181 | return false; | ||
182 | } | ||
183 | if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) { | ||
184 | wlr_log(L_ERROR, "Failed to read background image: %s." | ||
185 | #ifndef HAVE_GDK_PIXBUF | ||
186 | "\nSway was compiled without gdk_pixbuf support, so only" | ||
187 | "\nPNG images can be loaded. This is the likely cause." | ||
188 | #endif //HAVE_GDK_PIXBUF | ||
189 | , cairo_status_to_string( | ||
190 | cairo_surface_status(state->context.image))); | ||
191 | return false; | 92 | return false; |
192 | } | 93 | } |
193 | return true; | 94 | return true; |
@@ -294,24 +195,10 @@ int main(int argc, const char **argv) { | |||
294 | args.output_idx = atoi(argv[1]); | 195 | args.output_idx = atoi(argv[1]); |
295 | args.path = argv[2]; | 196 | args.path = argv[2]; |
296 | 197 | ||
297 | args.mode = BACKGROUND_MODE_STRETCH; | 198 | args.mode = parse_background_mode(argv[3]); |
298 | if (strcmp(argv[3], "stretch") == 0) { | 199 | if (args.mode == BACKGROUND_MODE_INVALID) { |
299 | args.mode = BACKGROUND_MODE_STRETCH; | ||
300 | } else if (strcmp(argv[3], "fill") == 0) { | ||
301 | args.mode = BACKGROUND_MODE_FILL; | ||
302 | } else if (strcmp(argv[3], "fit") == 0) { | ||
303 | args.mode = BACKGROUND_MODE_FIT; | ||
304 | } else if (strcmp(argv[3], "center") == 0) { | ||
305 | args.mode = BACKGROUND_MODE_CENTER; | ||
306 | } else if (strcmp(argv[3], "tile") == 0) { | ||
307 | args.mode = BACKGROUND_MODE_TILE; | ||
308 | } else if (strcmp(argv[3], "solid_color") == 0) { | ||
309 | args.mode = BACKGROUND_MODE_SOLID_COLOR; | ||
310 | } else { | ||
311 | wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]); | ||
312 | return 1; | 200 | return 1; |
313 | } | 201 | } |
314 | |||
315 | if (!prepare_context(&state)) { | 202 | if (!prepare_context(&state)) { |
316 | return 1; | 203 | return 1; |
317 | } | 204 | } |
@@ -345,10 +232,10 @@ int main(int argc, const char **argv) { | |||
345 | zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); | 232 | zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); |
346 | zwlr_layer_surface_v1_add_listener(state.layer_surface, | 233 | zwlr_layer_surface_v1_add_listener(state.layer_surface, |
347 | &layer_surface_listener, &state); | 234 | &layer_surface_listener, &state); |
348 | state.run_display = true; | ||
349 | wl_surface_commit(state.surface); | 235 | wl_surface_commit(state.surface); |
350 | wl_display_roundtrip(state.display); | 236 | wl_display_roundtrip(state.display); |
351 | 237 | ||
238 | state.run_display = true; | ||
352 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { | 239 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { |
353 | // This space intentionally left blank | 240 | // This space intentionally left blank |
354 | } | 241 | } |
diff --git a/swaylock/main.c b/swaylock/main.c index c2615951..1d522184 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -1,387 +1,144 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 700 |
2 | #include "wayland-swaylock-client-protocol.h" | 2 | #define _POSIX_C_SOURCE 200112L |
3 | #include <xkbcommon/xkbcommon.h> | 3 | #include <assert.h> |
4 | #include <xkbcommon/xkbcommon-names.h> | 4 | #include <ctype.h> |
5 | #include <security/pam_appl.h> | 5 | #include <fcntl.h> |
6 | #include <json-c/json.h> | 6 | #include <getopt.h> |
7 | #include <stdbool.h> | ||
7 | #include <stdio.h> | 8 | #include <stdio.h> |
8 | #include <stdlib.h> | 9 | #include <stdlib.h> |
9 | #include <string.h> | 10 | #include <string.h> |
10 | #include <sys/types.h> | 11 | #include <sys/stat.h> |
11 | #include <pwd.h> | 12 | #include <time.h> |
12 | #include <getopt.h> | ||
13 | #include <signal.h> | ||
14 | #include <stdbool.h> | ||
15 | #include <unistd.h> | 13 | #include <unistd.h> |
16 | #include "client/window.h" | 14 | #include <wayland-client.h> |
17 | #include "client/registry.h" | 15 | #include <wlr/util/log.h> |
18 | #include "client/cairo.h" | 16 | #include "swaylock/seat.h" |
19 | #include "swaylock/swaylock.h" | 17 | #include "swaylock/swaylock.h" |
20 | #include "ipc-client.h" | 18 | #include "background-image.h" |
21 | #include "log.h" | 19 | #include "pool-buffer.h" |
20 | #include "cairo.h" | ||
22 | #include "util.h" | 21 | #include "util.h" |
23 | 22 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" | |
24 | struct registry *registry; | 23 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
25 | struct render_data render_data; | 24 | |
26 | struct lock_config *config; | 25 | static void daemonize() { |
27 | bool show_indicator = true; | 26 | if (fork() == 0) { |
28 | 27 | int devnull = open("/dev/null", O_RDWR); | |
29 | void wl_dispatch_events() { | 28 | dup2(STDOUT_FILENO, devnull); |
30 | wl_display_flush(registry->display); | 29 | dup2(STDERR_FILENO, devnull); |
31 | if (wl_display_dispatch(registry->display) == -1) { | 30 | chdir("/"); |
32 | sway_log(L_ERROR, "failed to run wl_display_dispatch"); | 31 | } else { |
33 | exit(1); | 32 | exit(0); |
34 | } | 33 | } |
35 | } | 34 | } |
36 | 35 | ||
37 | void sigalarm_handler(int sig) { | 36 | static void layer_surface_configure(void *data, |
38 | signal(SIGALRM, SIG_IGN); | 37 | struct zwlr_layer_surface_v1 *layer_surface, |
39 | // Hide typing indicator | 38 | uint32_t serial, uint32_t width, uint32_t height) { |
40 | render_data.auth_state = AUTH_STATE_IDLE; | 39 | struct swaylock_surface *surface = data; |
41 | render(&render_data, config); | 40 | surface->width = width; |
42 | wl_display_flush(registry->display); | 41 | surface->height = height; |
43 | signal(SIGALRM, sigalarm_handler); | 42 | zwlr_layer_surface_v1_ack_configure(layer_surface, serial); |
43 | render_frame(surface); | ||
44 | } | 44 | } |
45 | 45 | ||
46 | void sway_terminate(int exit_code) { | 46 | static void layer_surface_closed(void *data, |
47 | int i; | 47 | struct zwlr_layer_surface_v1 *layer_surface) { |
48 | for (i = 0; i < render_data.surfaces->length; ++i) { | 48 | struct swaylock_surface *surface = data; |
49 | struct window *window = render_data.surfaces->items[i]; | 49 | zwlr_layer_surface_v1_destroy(surface->layer_surface); |
50 | window_teardown(window); | 50 | wl_surface_destroy(surface->surface); |
51 | } | 51 | surface->state->run_display = false; |
52 | list_free(render_data.surfaces); | ||
53 | if (registry) { | ||
54 | registry_teardown(registry); | ||
55 | } | ||
56 | exit(exit_code); | ||
57 | } | 52 | } |
58 | 53 | ||
59 | char *password; | 54 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
60 | int password_size; | 55 | .configure = layer_surface_configure, |
61 | enum line_source line_source = LINE_SOURCE_DEFAULT; | 56 | .closed = layer_surface_closed, |
57 | }; | ||
62 | 58 | ||
63 | struct lock_config *init_config() { | 59 | static void output_geometry(void *data, struct wl_output *output, int32_t x, |
64 | struct lock_config *config = calloc(1, sizeof(struct lock_config)); | 60 | int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, |
65 | 61 | const char *make, const char *model, int32_t transform) { | |
66 | config->font = strdup("sans-serif"); | 62 | // Who cares |
67 | config->colors.text = 0x000000FF; | ||
68 | |||
69 | config->colors.line = 0x000000FF; | ||
70 | config->colors.separator = 0x000000FF; | ||
71 | |||
72 | config->colors.input_cursor = 0x33DB00FF; | ||
73 | config->colors.backspace_cursor = 0xDB3300FF; | ||
74 | |||
75 | config->colors.normal.inner_ring = 0x000000BF; | ||
76 | config->colors.normal.outer_ring = 0x337D00FF; | ||
77 | |||
78 | config->colors.validating.inner_ring = 0x0072FFBF; | ||
79 | config->colors.validating.outer_ring = 0x3300FAFF; | ||
80 | |||
81 | config->colors.invalid.inner_ring = 0xFA0000BF; | ||
82 | config->colors.invalid.outer_ring = 0x7D3300FF; | ||
83 | |||
84 | config->radius = 50; | ||
85 | config->thickness = 10; | ||
86 | |||
87 | return config; | ||
88 | } | 63 | } |
89 | 64 | ||
90 | void free_config(struct lock_config *config) { | 65 | static void output_mode(void *data, struct wl_output *output, uint32_t flags, |
91 | free(config->font); | 66 | int32_t width, int32_t height, int32_t refresh) { |
92 | free(config); | 67 | // Who cares |
93 | } | 68 | } |
94 | 69 | ||
95 | int function_conversation(int num_msg, const struct pam_message **msg, | 70 | static void output_done(void *data, struct wl_output *output) { |
96 | struct pam_response **resp, void *appdata_ptr) { | 71 | // Who cares |
97 | |||
98 | const char* msg_style_names[] = { | ||
99 | NULL, | ||
100 | "PAM_PROMPT_ECHO_OFF", | ||
101 | "PAM_PROMPT_ECHO_ON", | ||
102 | "PAM_ERROR_MSG", | ||
103 | "PAM_TEXT_INFO", | ||
104 | }; | ||
105 | |||
106 | /* PAM expects an array of responses, one for each message */ | ||
107 | struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response)); | ||
108 | *resp = pam_reply; | ||
109 | |||
110 | for(int i=0; i<num_msg; ++i) { | ||
111 | sway_log(L_DEBUG, "msg[%d]: (%s) %s", i, | ||
112 | msg_style_names[msg[i]->msg_style], | ||
113 | msg[i]->msg); | ||
114 | |||
115 | switch (msg[i]->msg_style) { | ||
116 | case PAM_PROMPT_ECHO_OFF: | ||
117 | case PAM_PROMPT_ECHO_ON: | ||
118 | pam_reply[i].resp = password; | ||
119 | break; | ||
120 | |||
121 | case PAM_ERROR_MSG: | ||
122 | case PAM_TEXT_INFO: | ||
123 | break; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return PAM_SUCCESS; | ||
128 | } | 72 | } |
129 | 73 | ||
130 | /** | 74 | static void output_scale(void *data, struct wl_output *output, int32_t factor) { |
131 | * Note: PAM will free() 'password' during the process | 75 | struct swaylock_surface *surface = data; |
132 | */ | 76 | surface->scale = factor; |
133 | bool verify_password() { | 77 | if (surface->state->run_display) { |
134 | struct passwd *passwd = getpwuid(getuid()); | 78 | render_frames(surface->state); |
135 | char *username = passwd->pw_name; | ||
136 | |||
137 | const struct pam_conv local_conversation = { function_conversation, NULL }; | ||
138 | pam_handle_t *local_auth_handle = NULL; | ||
139 | int pam_err; | ||
140 | if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { | ||
141 | sway_abort("PAM returned %d\n", pam_err); | ||
142 | } | ||
143 | if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { | ||
144 | return false; | ||
145 | } | 79 | } |
146 | if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { | ||
147 | return false; | ||
148 | } | ||
149 | return true; | ||
150 | } | 80 | } |
151 | 81 | ||
152 | void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { | 82 | struct wl_output_listener output_listener = { |
153 | int redraw_screen = 0; | 83 | .geometry = output_geometry, |
154 | char *password_realloc; | 84 | .mode = output_mode, |
155 | int i; | 85 | .done = output_done, |
156 | 86 | .scale = output_scale, | |
157 | if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { | 87 | }; |
158 | switch (sym) { | 88 | |
159 | case XKB_KEY_KP_Enter: | 89 | static void handle_global(void *data, struct wl_registry *registry, |
160 | case XKB_KEY_Return: | 90 | uint32_t name, const char *interface, uint32_t version) { |
161 | render_data.auth_state = AUTH_STATE_VALIDATING; | 91 | struct swaylock_state *state = data; |
162 | 92 | if (strcmp(interface, wl_compositor_interface.name) == 0) { | |
163 | render(&render_data, config); | 93 | state->compositor = wl_registry_bind(registry, name, |
164 | // Make sure our render call will actually be displayed on the screen | 94 | &wl_compositor_interface, 3); |
165 | wl_dispatch_events(); | 95 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { |
166 | 96 | state->shm = wl_registry_bind(registry, name, | |
167 | if (verify_password()) { | 97 | &wl_shm_interface, 1); |
168 | exit(0); | 98 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { |
169 | } | 99 | struct wl_seat *seat = wl_registry_bind( |
170 | 100 | registry, name, &wl_seat_interface, 1); | |
171 | render_data.auth_state = AUTH_STATE_INVALID; | 101 | wl_seat_add_listener(seat, &seat_listener, state); |
172 | redraw_screen = 1; | 102 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
173 | 103 | state->layer_shell = wl_registry_bind( | |
174 | password_size = 1024; | 104 | registry, name, &zwlr_layer_shell_v1_interface, 1); |
175 | password = malloc(password_size); | 105 | } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { |
176 | password[0] = '\0'; | 106 | state->input_inhibit_manager = wl_registry_bind( |
177 | break; | 107 | registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); |
178 | case XKB_KEY_BackSpace: | 108 | } else if (strcmp(interface, wl_output_interface.name) == 0) { |
179 | i = strlen(password); | 109 | struct swaylock_surface *surface = |
180 | if (i > 0) { | 110 | calloc(1, sizeof(struct swaylock_surface)); |
181 | password[i - 1] = '\0'; | 111 | surface->state = state; |
182 | render_data.auth_state = AUTH_STATE_BACKSPACE; | 112 | surface->output = wl_registry_bind(registry, name, |
183 | redraw_screen = 1; | 113 | &wl_output_interface, 3); |
184 | } | 114 | wl_output_add_listener(surface->output, &output_listener, surface); |
185 | break; | 115 | wl_list_insert(&state->surfaces, &surface->link); |
186 | case XKB_KEY_Control_L: | ||
187 | case XKB_KEY_Control_R: | ||
188 | case XKB_KEY_Shift_L: | ||
189 | case XKB_KEY_Shift_R: | ||
190 | case XKB_KEY_Caps_Lock: | ||
191 | case XKB_KEY_Shift_Lock: | ||
192 | case XKB_KEY_Meta_L: | ||
193 | case XKB_KEY_Meta_R: | ||
194 | case XKB_KEY_Alt_L: | ||
195 | case XKB_KEY_Alt_R: | ||
196 | case XKB_KEY_Super_L: | ||
197 | case XKB_KEY_Super_R: | ||
198 | case XKB_KEY_Hyper_L: | ||
199 | case XKB_KEY_Hyper_R: | ||
200 | break; // don't draw screen on modifier keys | ||
201 | case XKB_KEY_Escape: | ||
202 | case XKB_KEY_u: | ||
203 | case XKB_KEY_U: | ||
204 | // clear password buffer on ctrl-u (or escape for i3lock compatibility) | ||
205 | if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state, | ||
206 | XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) { | ||
207 | render_data.auth_state = AUTH_STATE_BACKSPACE; | ||
208 | redraw_screen = 1; | ||
209 | |||
210 | password_size = 1024; | ||
211 | free(password); | ||
212 | password = malloc(password_size); | ||
213 | password[0] = '\0'; | ||
214 | break; | ||
215 | } | ||
216 | /* fallthrough */ | ||
217 | default: | ||
218 | render_data.auth_state = AUTH_STATE_INPUT; | ||
219 | redraw_screen = 1; | ||
220 | i = strlen(password); | ||
221 | if (i + 1 == password_size) { | ||
222 | password_size += 1024; | ||
223 | password_realloc = realloc(password, password_size); | ||
224 | // reset password if realloc fails. | ||
225 | if (password_realloc == NULL) { | ||
226 | password_size = 1024; | ||
227 | free(password); | ||
228 | password = malloc(password_size); | ||
229 | password[0] = '\0'; | ||
230 | break; | ||
231 | } else { | ||
232 | password = password_realloc; | ||
233 | } | ||
234 | } | ||
235 | password[i] = (char)codepoint; | ||
236 | password[i + 1] = '\0'; | ||
237 | break; | ||
238 | } | ||
239 | if (redraw_screen) { | ||
240 | render(&render_data, config); | ||
241 | wl_dispatch_events(); | ||
242 | // Hide the indicator after a couple of seconds | ||
243 | alarm(5); | ||
244 | } | ||
245 | } | 116 | } |
246 | } | 117 | } |
247 | 118 | ||
248 | void render_color(struct window *window, uint32_t color) { | 119 | static void handle_global_remove(void *data, struct wl_registry *registry, |
249 | cairo_set_source_u32(window->cairo, color); | 120 | uint32_t name) { |
250 | cairo_paint(window->cairo); | 121 | // who cares |
251 | } | 122 | } |
252 | 123 | ||
253 | void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) { | 124 | static const struct wl_registry_listener registry_listener = { |
254 | double width = cairo_image_surface_get_width(image); | 125 | .global = handle_global, |
255 | double height = cairo_image_surface_get_height(image); | 126 | .global_remove = handle_global_remove, |
256 | int wwidth = window->width * window->scale; | 127 | }; |
257 | int wheight = window->height * window->scale; | ||
258 | |||
259 | switch (scaling_mode) { | ||
260 | case SCALING_MODE_STRETCH: | ||
261 | cairo_scale(window->cairo, | ||
262 | (double) wwidth / width, | ||
263 | (double) wheight / height); | ||
264 | cairo_set_source_surface(window->cairo, image, 0, 0); | ||
265 | break; | ||
266 | case SCALING_MODE_FILL: | ||
267 | { | ||
268 | double window_ratio = (double) wwidth / wheight; | ||
269 | double bg_ratio = width / height; | ||
270 | 128 | ||
271 | if (window_ratio > bg_ratio) { | 129 | static struct swaylock_state state; |
272 | double scale = (double) wwidth / width; | ||
273 | cairo_scale(window->cairo, scale, scale); | ||
274 | cairo_set_source_surface(window->cairo, image, | ||
275 | 0, | ||
276 | (double) wheight/2 / scale - height/2); | ||
277 | } else { | ||
278 | double scale = (double) wheight / height; | ||
279 | cairo_scale(window->cairo, scale, scale); | ||
280 | cairo_set_source_surface(window->cairo, image, | ||
281 | (double) wwidth/2 / scale - width/2, | ||
282 | 0); | ||
283 | } | ||
284 | break; | ||
285 | } | ||
286 | case SCALING_MODE_FIT: | ||
287 | { | ||
288 | double window_ratio = (double) wwidth / wheight; | ||
289 | double bg_ratio = width / height; | ||
290 | |||
291 | if (window_ratio > bg_ratio) { | ||
292 | double scale = (double) wheight / height; | ||
293 | cairo_scale(window->cairo, scale, scale); | ||
294 | cairo_set_source_surface(window->cairo, image, | ||
295 | (double) wwidth/2 / scale - width/2, | ||
296 | 0); | ||
297 | } else { | ||
298 | double scale = (double) wwidth / width; | ||
299 | cairo_scale(window->cairo, scale, scale); | ||
300 | cairo_set_source_surface(window->cairo, image, | ||
301 | 0, | ||
302 | (double) wheight/2 / scale - height/2); | ||
303 | } | ||
304 | break; | ||
305 | } | ||
306 | case SCALING_MODE_CENTER: | ||
307 | cairo_set_source_surface(window->cairo, image, | ||
308 | (double) wwidth/2 - width/2, | ||
309 | (double) wheight/2 - height/2); | ||
310 | break; | ||
311 | case SCALING_MODE_TILE: | ||
312 | { | ||
313 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | ||
314 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); | ||
315 | cairo_set_source(window->cairo, pattern); | ||
316 | break; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | cairo_paint(window->cairo); | ||
321 | } | ||
322 | |||
323 | cairo_surface_t *load_image(char *image_path) { | ||
324 | cairo_surface_t *image = NULL; | ||
325 | |||
326 | #ifdef WITH_GDK_PIXBUF | ||
327 | GError *err = NULL; | ||
328 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); | ||
329 | if (!pixbuf) { | ||
330 | sway_abort("Failed to load background image: %s", err->message); | ||
331 | } | ||
332 | image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); | ||
333 | g_object_unref(pixbuf); | ||
334 | #else | ||
335 | image = cairo_image_surface_create_from_png(image_path); | ||
336 | #endif //WITH_GDK_PIXBUF | ||
337 | if (!image) { | ||
338 | sway_abort("Failed to read background image."); | ||
339 | } | ||
340 | |||
341 | return image; | ||
342 | } | ||
343 | 130 | ||
344 | int main(int argc, char **argv) { | 131 | int main(int argc, char **argv) { |
345 | const char *scaling_mode_str = "fit", *socket_path = NULL; | ||
346 | int i; | ||
347 | void *images = NULL; | ||
348 | config = init_config(); | ||
349 | |||
350 | render_data.num_images = 0; | ||
351 | render_data.color_set = 0; | ||
352 | render_data.color = 0xFFFFFFFF; | ||
353 | render_data.auth_state = AUTH_STATE_IDLE; | ||
354 | |||
355 | init_log(L_INFO); | ||
356 | // Install SIGALARM handler (for hiding the typing indicator) | ||
357 | signal(SIGALRM, sigalarm_handler); | ||
358 | |||
359 | static struct option long_options[] = { | 132 | static struct option long_options[] = { |
360 | {"help", no_argument, NULL, 'h'}, | 133 | {"help", no_argument, NULL, 'h'}, |
361 | {"color", required_argument, NULL, 'c'}, | 134 | {"color", required_argument, NULL, 'c'}, |
362 | {"image", required_argument, NULL, 'i'}, | 135 | {"image", required_argument, NULL, 'i'}, |
363 | {"scaling", required_argument, NULL, 0}, | 136 | {"scaling", required_argument, NULL, 's'}, |
364 | {"tiling", no_argument, NULL, 't'}, | 137 | {"tiling", no_argument, NULL, 't'}, |
365 | {"version", no_argument, NULL, 'v'}, | 138 | {"version", no_argument, NULL, 'v'}, |
366 | {"socket", required_argument, NULL, 'p'}, | 139 | {"socket", required_argument, NULL, 'p'}, |
367 | {"no-unlock-indicator", no_argument, NULL, 'u'}, | 140 | {"no-unlock-indicator", no_argument, NULL, 'u'}, |
368 | {"daemonize", no_argument, NULL, 'f'}, | 141 | {"daemonize", no_argument, NULL, 'f'}, |
369 | {"font", required_argument, NULL, 0}, | ||
370 | {"line-uses-ring", no_argument, NULL, 'r'}, | ||
371 | {"line-uses-inside", no_argument, NULL, 's'}, | ||
372 | {"textcolor", required_argument, NULL, 0}, | ||
373 | {"insidevercolor", required_argument, NULL, 0}, | ||
374 | {"insidewrongcolor", required_argument, NULL, 0}, | ||
375 | {"insidecolor", required_argument, NULL, 0}, | ||
376 | {"ringvercolor", required_argument, NULL, 0}, | ||
377 | {"ringwrongcolor", required_argument, NULL, 0}, | ||
378 | {"ringcolor", required_argument, NULL, 0}, | ||
379 | {"linecolor", required_argument, NULL, 0}, | ||
380 | {"separatorcolor", required_argument, NULL, 0}, | ||
381 | {"keyhlcolor", required_argument, NULL, 0}, | ||
382 | {"bshlcolor", required_argument, NULL, 0}, | ||
383 | {"indicator-radius", required_argument, NULL, 0}, | ||
384 | {"indicator-thickness", required_argument, NULL, 0}, | ||
385 | {0, 0, 0, 0} | 142 | {0, 0, 0, 0} |
386 | }; | 143 | }; |
387 | 144 | ||
@@ -390,415 +147,124 @@ int main(int argc, char **argv) { | |||
390 | "\n" | 147 | "\n" |
391 | " -h, --help Show help message and quit.\n" | 148 | " -h, --help Show help message and quit.\n" |
392 | " -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n" | 149 | " -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n" |
393 | " --scaling Scaling mode: stretch, fill, fit, center, tile.\n" | 150 | " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n" |
394 | " -t, --tiling Same as --scaling=tile.\n" | 151 | " -t, --tiling Same as --scaling=tile.\n" |
395 | " -v, --version Show the version number and quit.\n" | 152 | " -v, --version Show the version number and quit.\n" |
396 | " -i, --image [<output>:]<path> Display the given image.\n" | 153 | " -i, --image [<output>:]<path> Display the given image.\n" |
397 | " -u, --no-unlock-indicator Disable the unlock indicator.\n" | 154 | " -u, --no-unlock-indicator Disable the unlock indicator.\n" |
398 | " -f, --daemonize Detach from the controlling terminal.\n" | 155 | " -f, --daemonize Detach from the controlling terminal.\n" |
399 | " --socket <socket> Use the specified socket.\n" | 156 | " --socket <socket> Use the specified socket.\n"; |
400 | " For more information see `man swaylock`\n"; | ||
401 | 157 | ||
402 | 158 | struct swaylock_args args = { | |
403 | registry = registry_poll(); | 159 | .mode = BACKGROUND_MODE_SOLID_COLOR, |
160 | .color = 0xFFFFFFFF, | ||
161 | .show_indicator = true, | ||
162 | }; | ||
163 | cairo_surface_t *background_image = NULL; | ||
164 | state.args = args; | ||
165 | wlr_log_init(L_DEBUG, NULL); | ||
404 | 166 | ||
405 | int c; | 167 | int c; |
406 | while (1) { | 168 | while (1) { |
407 | int option_index = 0; | 169 | int option_index = 0; |
408 | c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index); | 170 | c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index); |
409 | if (c == -1) { | 171 | if (c == -1) { |
410 | break; | 172 | break; |
411 | } | 173 | } |
412 | switch (c) { | 174 | switch (c) { |
413 | case 'c': | 175 | case 'c': { |
414 | { | 176 | state.args.color = parse_color(optarg); |
415 | render_data.color = parse_color(optarg); | 177 | state.args.mode = BACKGROUND_MODE_SOLID_COLOR; |
416 | render_data.color_set = 1; | ||
417 | break; | 178 | break; |
418 | } | 179 | } |
419 | case 'i': | 180 | case 'i': |
420 | { | 181 | // TODO: Multiple background images (bleh) |
421 | char *image_path = strchr(optarg, ':'); | 182 | background_image = load_background_image(optarg); |
422 | if (image_path == NULL) { | 183 | if (!background_image) { |
423 | if (render_data.num_images == 0) { | 184 | return 1; |
424 | // Provided image without output | ||
425 | render_data.image = load_image(optarg); | ||
426 | render_data.num_images = -1; | ||
427 | } else { | ||
428 | sway_log(L_ERROR, "output must be defined for all --images or no --images"); | ||
429 | exit(EXIT_FAILURE); | ||
430 | } | ||
431 | } else { | ||
432 | // Provided image for all outputs | ||
433 | if (render_data.num_images == 0) { | ||
434 | images = calloc(registry->outputs->length, sizeof(char*) * 2); | ||
435 | } else if (render_data.num_images == -1) { | ||
436 | sway_log(L_ERROR, "output must be defined for all --images or no --images"); | ||
437 | exit(EXIT_FAILURE); | ||
438 | } | ||
439 | |||
440 | image_path[0] = '\0'; | ||
441 | ((char**) images)[render_data.num_images * 2] = optarg; | ||
442 | ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path; | ||
443 | } | 185 | } |
186 | state.args.mode = BACKGROUND_MODE_FILL; | ||
444 | break; | 187 | break; |
445 | } | 188 | case 's': |
446 | case 't': | 189 | state.args.mode = parse_background_mode(optarg); |
447 | scaling_mode_str = "tile"; | 190 | if (state.args.mode == BACKGROUND_MODE_INVALID) { |
191 | return 1; | ||
192 | } | ||
448 | break; | 193 | break; |
449 | case 'p': | 194 | case 't': |
450 | socket_path = optarg; | 195 | state.args.mode = BACKGROUND_MODE_TILE; |
451 | break; | 196 | break; |
452 | case 'v': | 197 | case 'v': |
453 | fprintf(stdout, "swaylock version " SWAY_VERSION "\n"); | 198 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE |
454 | exit(EXIT_SUCCESS); | 199 | fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n", |
455 | break; | 200 | SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); |
201 | #else | ||
202 | fprintf(stdout, "version unknown\n"); | ||
203 | #endif | ||
204 | return 0; | ||
456 | case 'u': | 205 | case 'u': |
457 | show_indicator = false; | 206 | state.args.show_indicator = false; |
458 | break; | ||
459 | case 'f': { | ||
460 | pid_t t = fork(); | ||
461 | if (t == -1) { | ||
462 | sway_log(L_ERROR, "daemon call failed"); | ||
463 | exit(EXIT_FAILURE); | ||
464 | } else if (t > 0) { | ||
465 | exit(0); | ||
466 | } | ||
467 | break; | ||
468 | } | ||
469 | case 'r': | ||
470 | if (line_source != LINE_SOURCE_DEFAULT) { | ||
471 | sway_log(L_ERROR, "line source options conflict"); | ||
472 | exit(EXIT_FAILURE); | ||
473 | } | ||
474 | line_source = LINE_SOURCE_RING; | ||
475 | break; | 207 | break; |
476 | case 's': | 208 | case 'f': |
477 | if (line_source != LINE_SOURCE_DEFAULT) { | 209 | daemonize(); |
478 | sway_log(L_ERROR, "line source options conflict"); | ||
479 | exit(EXIT_FAILURE); | ||
480 | } | ||
481 | line_source = LINE_SOURCE_INSIDE; | ||
482 | break; | ||
483 | case 0: | ||
484 | if (strcmp(long_options[option_index].name, "font") == 0) { | ||
485 | free(config->font); | ||
486 | config->font = strdup(optarg); | ||
487 | } else if (strcmp(long_options[option_index].name, "scaling") == 0) { | ||
488 | scaling_mode_str = optarg; | ||
489 | } else if (strcmp(long_options[option_index].name, "textcolor") == 0) { | ||
490 | config->colors.text = parse_color(optarg); | ||
491 | } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) { | ||
492 | config->colors.validating.inner_ring = parse_color(optarg); | ||
493 | } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) { | ||
494 | config->colors.invalid.inner_ring = parse_color(optarg); | ||
495 | } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) { | ||
496 | config->colors.normal.inner_ring = parse_color(optarg); | ||
497 | } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) { | ||
498 | config->colors.validating.outer_ring = parse_color(optarg); | ||
499 | } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) { | ||
500 | config->colors.invalid.outer_ring = parse_color(optarg); | ||
501 | } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) { | ||
502 | config->colors.normal.outer_ring = parse_color(optarg); | ||
503 | } else if (strcmp(long_options[option_index].name, "linecolor") == 0) { | ||
504 | config->colors.line = parse_color(optarg); | ||
505 | } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) { | ||
506 | config->colors.separator = parse_color(optarg); | ||
507 | } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) { | ||
508 | config->colors.input_cursor = parse_color(optarg); | ||
509 | } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) { | ||
510 | config->colors.backspace_cursor = parse_color(optarg); | ||
511 | } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) { | ||
512 | config->radius = atoi(optarg); | ||
513 | } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) { | ||
514 | config->thickness = atoi(optarg); | ||
515 | } | ||
516 | break; | 210 | break; |
517 | default: | 211 | default: |
518 | fprintf(stderr, "%s", usage); | 212 | fprintf(stderr, "%s", usage); |
519 | exit(EXIT_FAILURE); | 213 | return 1; |
520 | } | 214 | } |
521 | } | 215 | } |
522 | 216 | ||
523 | render_data.scaling_mode = SCALING_MODE_STRETCH; | 217 | wl_list_init(&state.surfaces); |
524 | if (strcmp(scaling_mode_str, "stretch") == 0) { | 218 | state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); |
525 | render_data.scaling_mode = SCALING_MODE_STRETCH; | 219 | state.display = wl_display_connect(NULL); |
526 | } else if (strcmp(scaling_mode_str, "fill") == 0) { | 220 | assert(state.display); |
527 | render_data.scaling_mode = SCALING_MODE_FILL; | 221 | |
528 | } else if (strcmp(scaling_mode_str, "fit") == 0) { | 222 | struct wl_registry *registry = wl_display_get_registry(state.display); |
529 | render_data.scaling_mode = SCALING_MODE_FIT; | 223 | wl_registry_add_listener(registry, ®istry_listener, &state); |
530 | } else if (strcmp(scaling_mode_str, "center") == 0) { | 224 | wl_display_roundtrip(state.display); |
531 | render_data.scaling_mode = SCALING_MODE_CENTER; | 225 | assert(state.compositor && state.layer_shell && state.shm); |
532 | } else if (strcmp(scaling_mode_str, "tile") == 0) { | 226 | if (!state.input_inhibit_manager) { |
533 | render_data.scaling_mode = SCALING_MODE_TILE; | 227 | wlr_log(L_ERROR, "Compositor does not support the input inhibitor " |
534 | } else { | 228 | "protocol, refusing to run insecurely"); |
535 | sway_abort("Unsupported scaling mode: %s", scaling_mode_str); | ||
536 | } | 229 | } |
537 | 230 | ||
538 | password_size = 1024; | 231 | if (wl_list_empty(&state.surfaces)) { |
539 | password = malloc(password_size); | 232 | wlr_log(L_DEBUG, "Exiting - no outputs to show on."); |
540 | password[0] = '\0'; | 233 | return 0; |
541 | render_data.surfaces = create_list(); | ||
542 | if (!socket_path) { | ||
543 | socket_path = get_socketpath(); | ||
544 | if (!socket_path) { | ||
545 | sway_abort("Unable to retrieve socket path"); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | if (!registry) { | ||
550 | sway_abort("Unable to connect to wayland compositor"); | ||
551 | } | ||
552 | |||
553 | if (!registry->swaylock) { | ||
554 | sway_abort("swaylock requires the compositor to support the swaylock extension."); | ||
555 | } | ||
556 | |||
557 | if (registry->pointer) { | ||
558 | // We don't want swaylock to have a pointer | ||
559 | wl_pointer_destroy(registry->pointer); | ||
560 | registry->pointer = NULL; | ||
561 | } | ||
562 | |||
563 | for (i = 0; i < registry->outputs->length; ++i) { | ||
564 | struct output_state *output = registry->outputs->items[i]; | ||
565 | struct window *window = window_setup(registry, | ||
566 | output->width, output->height, output->scale, true); | ||
567 | if (!window) { | ||
568 | sway_abort("Failed to create surfaces."); | ||
569 | } | ||
570 | list_add(render_data.surfaces, window); | ||
571 | } | ||
572 | |||
573 | registry->input->notify = notify_key; | ||
574 | |||
575 | // Different background for the output | ||
576 | if (render_data.num_images >= 1) { | ||
577 | char **displays_paths = images; | ||
578 | render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*)); | ||
579 | |||
580 | int socketfd = ipc_open_socket(socket_path); | ||
581 | uint32_t len = 0; | ||
582 | char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len); | ||
583 | struct json_object *json_outputs = json_tokener_parse(outputs); | ||
584 | |||
585 | for (i = 0; i < registry->outputs->length; ++i) { | ||
586 | if (displays_paths[i * 2] != NULL) { | ||
587 | for (int j = 0;; ++j) { | ||
588 | if (j >= json_object_array_length(json_outputs)) { | ||
589 | sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]); | ||
590 | exit(EXIT_FAILURE); | ||
591 | } | ||
592 | |||
593 | struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j); | ||
594 | if (!json_object_object_get_ex(at_j, "name", &dsp_name)) { | ||
595 | sway_abort("output doesn't have a name field"); | ||
596 | } | ||
597 | if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) { | ||
598 | render_data.images[j] = load_image(displays_paths[i * 2 + 1]); | ||
599 | break; | ||
600 | } | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | |||
605 | json_object_put(json_outputs); | ||
606 | close(socketfd); | ||
607 | free(displays_paths); | ||
608 | } | 234 | } |
609 | 235 | ||
610 | render(&render_data, config); | 236 | struct swaylock_surface *surface; |
611 | bool locked = false; | 237 | wl_list_for_each(surface, &state.surfaces, link) { |
612 | while (wl_display_dispatch(registry->display) != -1) { | 238 | surface->image = background_image; |
613 | if (!locked) { | 239 | |
614 | for (i = 0; i < registry->outputs->length; ++i) { | 240 | surface->surface = wl_compositor_create_surface(state.compositor); |
615 | struct output_state *output = registry->outputs->items[i]; | 241 | assert(surface->surface); |
616 | struct window *window = render_data.surfaces->items[i]; | 242 | |
617 | lock_set_lock_surface(registry->swaylock, output->output, window->surface); | 243 | surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( |
618 | } | 244 | state.layer_shell, surface->surface, surface->output, |
619 | locked = true; | 245 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); |
620 | } | 246 | assert(surface->layer_surface); |
247 | |||
248 | zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); | ||
249 | zwlr_layer_surface_v1_set_anchor(surface->layer_surface, | ||
250 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
251 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
252 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
253 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
254 | zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); | ||
255 | zwlr_layer_surface_v1_set_keyboard_interactivity( | ||
256 | surface->layer_surface, true); | ||
257 | zwlr_layer_surface_v1_add_listener(surface->layer_surface, | ||
258 | &layer_surface_listener, surface); | ||
259 | wl_surface_commit(surface->surface); | ||
260 | wl_display_roundtrip(state.display); | ||
621 | } | 261 | } |
622 | 262 | ||
623 | // Free surfaces | 263 | zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); |
624 | if (render_data.num_images == -1) { | ||
625 | cairo_surface_destroy(render_data.image); | ||
626 | } else if (render_data.num_images >= 1) { | ||
627 | for (i = 0; i < registry->outputs->length; ++i) { | ||
628 | if (render_data.images[i] != NULL) { | ||
629 | cairo_surface_destroy(render_data.images[i]); | ||
630 | } | ||
631 | } | ||
632 | free(render_data.images); | ||
633 | } | ||
634 | 264 | ||
635 | for (i = 0; i < render_data.surfaces->length; ++i) { | 265 | state.run_display = true; |
636 | struct window *window = render_data.surfaces->items[i]; | 266 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { |
637 | window_teardown(window); | 267 | // This space intentionally left blank |
638 | } | 268 | } |
639 | list_free(render_data.surfaces); | ||
640 | registry_teardown(registry); | ||
641 | |||
642 | free_config(config); | ||
643 | |||
644 | return 0; | 269 | return 0; |
645 | } | 270 | } |
646 | |||
647 | void render(struct render_data *render_data, struct lock_config *config) { | ||
648 | int i; | ||
649 | for (i = 0; i < render_data->surfaces->length; ++i) { | ||
650 | sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length); | ||
651 | struct window *window = render_data->surfaces->items[i]; | ||
652 | if (!window_prerender(window) || !window->cairo) { | ||
653 | continue; | ||
654 | } | ||
655 | int wwidth = window->width * window->scale; | ||
656 | int wheight = window->height * window->scale; | ||
657 | |||
658 | cairo_save(window->cairo); | ||
659 | cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); | ||
660 | cairo_paint(window->cairo); | ||
661 | cairo_restore(window->cairo); | ||
662 | |||
663 | // Reset the transformation matrix | ||
664 | cairo_identity_matrix(window->cairo); | ||
665 | |||
666 | if (render_data->num_images == 0 || render_data->color_set) { | ||
667 | render_color(window, render_data->color); | ||
668 | } | ||
669 | |||
670 | if (render_data->num_images == -1) { | ||
671 | // One background for all | ||
672 | render_image(window, render_data->image, render_data->scaling_mode); | ||
673 | } else if (render_data->num_images >= 1) { | ||
674 | // Different backgrounds | ||
675 | if (render_data->images[i] != NULL) { | ||
676 | render_image(window, render_data->images[i], render_data->scaling_mode); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | // Reset the transformation matrix again | ||
681 | cairo_identity_matrix(window->cairo); | ||
682 | |||
683 | // Draw specific values (copied from i3) | ||
684 | const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; | ||
685 | const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; | ||
686 | |||
687 | // Add visual indicator | ||
688 | if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) { | ||
689 | // Draw circle | ||
690 | cairo_set_line_width(window->cairo, config->thickness); | ||
691 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI); | ||
692 | switch (render_data->auth_state) { | ||
693 | case AUTH_STATE_INPUT: | ||
694 | case AUTH_STATE_BACKSPACE: { | ||
695 | cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); | ||
696 | cairo_fill_preserve(window->cairo); | ||
697 | cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); | ||
698 | cairo_stroke(window->cairo); | ||
699 | } break; | ||
700 | case AUTH_STATE_VALIDATING: { | ||
701 | cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); | ||
702 | cairo_fill_preserve(window->cairo); | ||
703 | cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); | ||
704 | cairo_stroke(window->cairo); | ||
705 | } break; | ||
706 | case AUTH_STATE_INVALID: { | ||
707 | cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); | ||
708 | cairo_fill_preserve(window->cairo); | ||
709 | cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); | ||
710 | cairo_stroke(window->cairo); | ||
711 | } break; | ||
712 | default: break; | ||
713 | } | ||
714 | |||
715 | // Draw a message | ||
716 | char *text = NULL; | ||
717 | cairo_set_source_u32(window->cairo, config->colors.text); | ||
718 | cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); | ||
719 | cairo_set_font_size(window->cairo, config->radius/3.0f); | ||
720 | switch (render_data->auth_state) { | ||
721 | case AUTH_STATE_VALIDATING: | ||
722 | text = "verifying"; | ||
723 | break; | ||
724 | case AUTH_STATE_INVALID: | ||
725 | text = "wrong"; | ||
726 | break; | ||
727 | default: break; | ||
728 | } | ||
729 | |||
730 | if (text) { | ||
731 | cairo_text_extents_t extents; | ||
732 | double x, y; | ||
733 | |||
734 | cairo_text_extents(window->cairo, text, &extents); | ||
735 | x = wwidth/2 - ((extents.width/2) + extents.x_bearing); | ||
736 | y = wheight/2 - ((extents.height/2) + extents.y_bearing); | ||
737 | |||
738 | cairo_move_to(window->cairo, x, y); | ||
739 | cairo_show_text(window->cairo, text); | ||
740 | cairo_close_path(window->cairo); | ||
741 | cairo_new_sub_path(window->cairo); | ||
742 | } | ||
743 | |||
744 | // Typing indicator: Highlight random part on keypress | ||
745 | if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) { | ||
746 | static double highlight_start = 0; | ||
747 | highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; | ||
748 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE); | ||
749 | if (render_data->auth_state == AUTH_STATE_INPUT) { | ||
750 | cairo_set_source_u32(window->cairo, config->colors.input_cursor); | ||
751 | } else { | ||
752 | cairo_set_source_u32(window->cairo, config->colors.backspace_cursor); | ||
753 | } | ||
754 | cairo_stroke(window->cairo); | ||
755 | |||
756 | // Draw borders | ||
757 | cairo_set_source_u32(window->cairo, config->colors.separator); | ||
758 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); | ||
759 | cairo_stroke(window->cairo); | ||
760 | |||
761 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS); | ||
762 | cairo_stroke(window->cairo); | ||
763 | } | ||
764 | |||
765 | switch(line_source) { | ||
766 | case LINE_SOURCE_RING: | ||
767 | switch(render_data->auth_state) { | ||
768 | case AUTH_STATE_VALIDATING: | ||
769 | cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring); | ||
770 | break; | ||
771 | case AUTH_STATE_INVALID: | ||
772 | cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring); | ||
773 | break; | ||
774 | default: | ||
775 | cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring); | ||
776 | } | ||
777 | break; | ||
778 | case LINE_SOURCE_INSIDE: | ||
779 | switch(render_data->auth_state) { | ||
780 | case AUTH_STATE_VALIDATING: | ||
781 | cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring); | ||
782 | break; | ||
783 | case AUTH_STATE_INVALID: | ||
784 | cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring); | ||
785 | break; | ||
786 | default: | ||
787 | cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring); | ||
788 | break; | ||
789 | } | ||
790 | break; | ||
791 | default: | ||
792 | cairo_set_source_u32(window->cairo, config->colors.line); | ||
793 | break; | ||
794 | } | ||
795 | // Draw inner + outer border of the circle | ||
796 | cairo_set_line_width(window->cairo, 2.0); | ||
797 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI); | ||
798 | cairo_stroke(window->cairo); | ||
799 | cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI); | ||
800 | cairo_stroke(window->cairo); | ||
801 | } | ||
802 | window_render(window); | ||
803 | } | ||
804 | } | ||
diff --git a/swaylock/meson.build b/swaylock/meson.build new file mode 100644 index 00000000..3cde47a4 --- /dev/null +++ b/swaylock/meson.build | |||
@@ -0,0 +1,23 @@ | |||
1 | executable( | ||
2 | 'swaylock', [ | ||
3 | 'main.c', | ||
4 | 'password.c', | ||
5 | 'render.c', | ||
6 | 'seat.c' | ||
7 | ], | ||
8 | include_directories: [sway_inc], | ||
9 | dependencies: [ | ||
10 | cairo, | ||
11 | client_protos, | ||
12 | gdk_pixbuf, | ||
13 | libpam, | ||
14 | math, | ||
15 | pango, | ||
16 | pangocairo, | ||
17 | xkbcommon, | ||
18 | wayland_client, | ||
19 | wlroots, | ||
20 | ], | ||
21 | link_with: [lib_sway_common, lib_sway_client], | ||
22 | install: true | ||
23 | ) | ||
diff --git a/swaylock/password.c b/swaylock/password.c new file mode 100644 index 00000000..1839f991 --- /dev/null +++ b/swaylock/password.c | |||
@@ -0,0 +1,126 @@ | |||
1 | #include <assert.h> | ||
2 | #include <pwd.h> | ||
3 | #include <security/pam_appl.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <unistd.h> | ||
6 | #include <wlr/util/log.h> | ||
7 | #include <xkbcommon/xkbcommon.h> | ||
8 | #include "swaylock/swaylock.h" | ||
9 | #include "swaylock/seat.h" | ||
10 | #include "unicode.h" | ||
11 | |||
12 | static int function_conversation(int num_msg, const struct pam_message **msg, | ||
13 | struct pam_response **resp, void *data) { | ||
14 | struct swaylock_password *pw = data; | ||
15 | /* PAM expects an array of responses, one for each message */ | ||
16 | struct pam_response *pam_reply = calloc( | ||
17 | num_msg, sizeof(struct pam_response)); | ||
18 | *resp = pam_reply; | ||
19 | for (int i = 0; i < num_msg; ++i) { | ||
20 | switch (msg[i]->msg_style) { | ||
21 | case PAM_PROMPT_ECHO_OFF: | ||
22 | case PAM_PROMPT_ECHO_ON: | ||
23 | pam_reply[i].resp = pw->buffer; | ||
24 | break; | ||
25 | case PAM_ERROR_MSG: | ||
26 | case PAM_TEXT_INFO: | ||
27 | break; | ||
28 | } | ||
29 | } | ||
30 | return PAM_SUCCESS; | ||
31 | } | ||
32 | |||
33 | static bool attempt_password(struct swaylock_password *pw) { | ||
34 | struct passwd *passwd = getpwuid(getuid()); | ||
35 | char *username = passwd->pw_name; | ||
36 | const struct pam_conv local_conversation = { | ||
37 | function_conversation, pw | ||
38 | }; | ||
39 | pam_handle_t *local_auth_handle = NULL; | ||
40 | int pam_err; | ||
41 | if ((pam_err = pam_start("swaylock", username, | ||
42 | &local_conversation, &local_auth_handle)) != PAM_SUCCESS) { | ||
43 | wlr_log(L_ERROR, "PAM returned error %d", pam_err); | ||
44 | } | ||
45 | if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) { | ||
46 | wlr_log(L_ERROR, "pam_authenticate failed"); | ||
47 | goto fail; | ||
48 | } | ||
49 | if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) { | ||
50 | wlr_log(L_ERROR, "pam_end failed"); | ||
51 | goto fail; | ||
52 | } | ||
53 | // PAM frees this | ||
54 | pw->buffer = NULL; | ||
55 | pw->len = pw->size = 0; | ||
56 | return true; | ||
57 | fail: | ||
58 | // PAM frees this | ||
59 | pw->buffer = NULL; | ||
60 | pw->len = pw->size = 0; | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | static bool backspace(struct swaylock_password *pw) { | ||
65 | if (pw->len != 0) { | ||
66 | pw->buffer[--pw->len] = 0; | ||
67 | return true; | ||
68 | } | ||
69 | return false; | ||
70 | } | ||
71 | |||
72 | static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { | ||
73 | if (!pw->buffer) { | ||
74 | pw->size = 8; | ||
75 | if (!(pw->buffer = malloc(pw->size))) { | ||
76 | // TODO: Display error | ||
77 | return; | ||
78 | } | ||
79 | pw->buffer[0] = 0; | ||
80 | } | ||
81 | size_t utf8_size = utf8_chsize(codepoint); | ||
82 | if (pw->len + utf8_size + 1 >= pw->size) { | ||
83 | size_t size = pw->size * 2; | ||
84 | char *buffer = realloc(pw->buffer, size); | ||
85 | if (!buffer) { | ||
86 | // TODO: Display error | ||
87 | return; | ||
88 | } | ||
89 | pw->size = size; | ||
90 | pw->buffer = buffer; | ||
91 | } | ||
92 | utf8_encode(&pw->buffer[pw->len], codepoint); | ||
93 | pw->buffer[pw->len + utf8_size] = 0; | ||
94 | pw->len += utf8_size; | ||
95 | } | ||
96 | |||
97 | void swaylock_handle_key(struct swaylock_state *state, | ||
98 | xkb_keysym_t keysym, uint32_t codepoint) { | ||
99 | switch (keysym) { | ||
100 | case XKB_KEY_KP_Enter: /* fallthrough */ | ||
101 | case XKB_KEY_Return: | ||
102 | state->auth_state = AUTH_STATE_VALIDATING; | ||
103 | render_frames(state); | ||
104 | wl_display_roundtrip(state->display); | ||
105 | if (attempt_password(&state->password)) { | ||
106 | state->run_display = false; | ||
107 | break; | ||
108 | } | ||
109 | state->auth_state = AUTH_STATE_INVALID; | ||
110 | render_frames(state); | ||
111 | break; | ||
112 | case XKB_KEY_BackSpace: | ||
113 | if (backspace(&state->password)) { | ||
114 | state->auth_state = AUTH_STATE_BACKSPACE; | ||
115 | render_frames(state); | ||
116 | } | ||
117 | break; | ||
118 | default: | ||
119 | if (codepoint) { | ||
120 | append_ch(&state->password, codepoint); | ||
121 | state->auth_state = AUTH_STATE_INPUT; | ||
122 | render_frames(state); | ||
123 | } | ||
124 | break; | ||
125 | } | ||
126 | } | ||
diff --git a/swaylock/render.c b/swaylock/render.c new file mode 100644 index 00000000..cd387be5 --- /dev/null +++ b/swaylock/render.c | |||
@@ -0,0 +1,150 @@ | |||
1 | #define _POSIX_C_SOURCE 199506L | ||
2 | #include <math.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <wayland-client.h> | ||
5 | #include "cairo.h" | ||
6 | #include "background-image.h" | ||
7 | #include "swaylock/swaylock.h" | ||
8 | |||
9 | #define M_PI 3.14159265358979323846 | ||
10 | const int ARC_RADIUS = 50; | ||
11 | const int ARC_THICKNESS = 10; | ||
12 | const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; | ||
13 | const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; | ||
14 | |||
15 | void render_frame(struct swaylock_surface *surface) { | ||
16 | struct swaylock_state *state = surface->state; | ||
17 | |||
18 | int buffer_width = surface->width * surface->scale; | ||
19 | int buffer_height = surface->height * surface->scale; | ||
20 | |||
21 | surface->current_buffer = get_next_buffer(state->shm, | ||
22 | surface->buffers, buffer_width, buffer_height); | ||
23 | cairo_t *cairo = surface->current_buffer->cairo; | ||
24 | cairo_identity_matrix(cairo); | ||
25 | |||
26 | if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { | ||
27 | cairo_set_source_u32(cairo, state->args.color); | ||
28 | cairo_paint(cairo); | ||
29 | } else { | ||
30 | render_background_image(cairo, surface->image, | ||
31 | state->args.mode, buffer_width, buffer_height); | ||
32 | } | ||
33 | cairo_identity_matrix(cairo); | ||
34 | |||
35 | int arc_radius = ARC_RADIUS * surface->scale; | ||
36 | int arc_thickness = ARC_THICKNESS * surface->scale; | ||
37 | float type_indicator_border_thickness = | ||
38 | TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; | ||
39 | |||
40 | if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { | ||
41 | // Draw circle | ||
42 | cairo_set_line_width(cairo, arc_thickness); | ||
43 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI); | ||
44 | switch (state->auth_state) { | ||
45 | case AUTH_STATE_INPUT: | ||
46 | case AUTH_STATE_BACKSPACE: { | ||
47 | cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); | ||
48 | cairo_fill_preserve(cairo); | ||
49 | cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0); | ||
50 | cairo_stroke(cairo); | ||
51 | } break; | ||
52 | case AUTH_STATE_VALIDATING: { | ||
53 | cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75); | ||
54 | cairo_fill_preserve(cairo); | ||
55 | cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255); | ||
56 | cairo_stroke(cairo); | ||
57 | } break; | ||
58 | case AUTH_STATE_INVALID: { | ||
59 | cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75); | ||
60 | cairo_fill_preserve(cairo); | ||
61 | cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0); | ||
62 | cairo_stroke(cairo); | ||
63 | } break; | ||
64 | default: break; | ||
65 | } | ||
66 | |||
67 | // Draw a message | ||
68 | char *text = NULL; | ||
69 | cairo_set_source_rgb(cairo, 0, 0, 0); | ||
70 | cairo_select_font_face(cairo, "sans-serif", | ||
71 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); | ||
72 | cairo_set_font_size(cairo, arc_radius / 3.0f); | ||
73 | switch (state->auth_state) { | ||
74 | case AUTH_STATE_VALIDATING: | ||
75 | text = "verifying"; | ||
76 | break; | ||
77 | case AUTH_STATE_INVALID: | ||
78 | text = "wrong"; | ||
79 | break; | ||
80 | default: break; | ||
81 | } | ||
82 | |||
83 | if (text) { | ||
84 | cairo_text_extents_t extents; | ||
85 | double x, y; | ||
86 | cairo_text_extents(cairo, text, &extents); | ||
87 | x = (buffer_width / 2) - | ||
88 | (extents.width / 2 + extents.x_bearing); | ||
89 | y = (buffer_height / 2) - | ||
90 | (extents.height / 2 + extents.y_bearing); | ||
91 | |||
92 | cairo_move_to(cairo, x, y); | ||
93 | cairo_show_text(cairo, text); | ||
94 | cairo_close_path(cairo); | ||
95 | cairo_new_sub_path(cairo); | ||
96 | } | ||
97 | |||
98 | // Typing indicator: Highlight random part on keypress | ||
99 | if (state->auth_state == AUTH_STATE_INPUT | ||
100 | || state->auth_state == AUTH_STATE_BACKSPACE) { | ||
101 | static double highlight_start = 0; | ||
102 | highlight_start += | ||
103 | (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; | ||
104 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
105 | arc_radius, highlight_start, | ||
106 | highlight_start + TYPE_INDICATOR_RANGE); | ||
107 | if (state->auth_state == AUTH_STATE_INPUT) { | ||
108 | cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); | ||
109 | } else { | ||
110 | cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); | ||
111 | } | ||
112 | cairo_stroke(cairo); | ||
113 | |||
114 | // Draw borders | ||
115 | cairo_set_source_rgb(cairo, 0, 0, 0); | ||
116 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
117 | arc_radius, highlight_start, | ||
118 | highlight_start + type_indicator_border_thickness); | ||
119 | cairo_stroke(cairo); | ||
120 | |||
121 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
122 | arc_radius, highlight_start + TYPE_INDICATOR_RANGE, | ||
123 | highlight_start + TYPE_INDICATOR_RANGE + | ||
124 | type_indicator_border_thickness); | ||
125 | cairo_stroke(cairo); | ||
126 | } | ||
127 | |||
128 | // Draw inner + outer border of the circle | ||
129 | cairo_set_source_rgb(cairo, 0, 0, 0); | ||
130 | cairo_set_line_width(cairo, 2.0 * surface->scale); | ||
131 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
132 | arc_radius - arc_thickness / 2, 0, 2 * M_PI); | ||
133 | cairo_stroke(cairo); | ||
134 | cairo_arc(cairo, buffer_width / 2, buffer_height / 2, | ||
135 | arc_radius + arc_thickness / 2, 0, 2 * M_PI); | ||
136 | cairo_stroke(cairo); | ||
137 | } | ||
138 | |||
139 | wl_surface_set_buffer_scale(surface->surface, surface->scale); | ||
140 | wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); | ||
141 | wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); | ||
142 | wl_surface_commit(surface->surface); | ||
143 | } | ||
144 | |||
145 | void render_frames(struct swaylock_state *state) { | ||
146 | struct swaylock_surface *surface; | ||
147 | wl_list_for_each(surface, &state->surfaces, link) { | ||
148 | render_frame(surface); | ||
149 | } | ||
150 | } | ||
diff --git a/swaylock/seat.c b/swaylock/seat.c new file mode 100644 index 00000000..21db7c4f --- /dev/null +++ b/swaylock/seat.c | |||
@@ -0,0 +1,190 @@ | |||
1 | #include <assert.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <sys/mman.h> | ||
4 | #include <unistd.h> | ||
5 | #include <wlr/util/log.h> | ||
6 | #include <xkbcommon/xkbcommon.h> | ||
7 | #include "swaylock/swaylock.h" | ||
8 | #include "swaylock/seat.h" | ||
9 | |||
10 | const char *XKB_MASK_NAMES[MASK_LAST] = { | ||
11 | XKB_MOD_NAME_SHIFT, | ||
12 | XKB_MOD_NAME_CAPS, | ||
13 | XKB_MOD_NAME_CTRL, | ||
14 | XKB_MOD_NAME_ALT, | ||
15 | "Mod2", | ||
16 | "Mod3", | ||
17 | XKB_MOD_NAME_LOGO, | ||
18 | "Mod5", | ||
19 | }; | ||
20 | |||
21 | const enum mod_bit XKB_MODS[MASK_LAST] = { | ||
22 | MOD_SHIFT, | ||
23 | MOD_CAPS, | ||
24 | MOD_CTRL, | ||
25 | MOD_ALT, | ||
26 | MOD_MOD2, | ||
27 | MOD_MOD3, | ||
28 | MOD_LOGO, | ||
29 | MOD_MOD5 | ||
30 | }; | ||
31 | |||
32 | static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, | ||
33 | uint32_t format, int32_t fd, uint32_t size) { | ||
34 | struct swaylock_state *state = data; | ||
35 | if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { | ||
36 | close(fd); | ||
37 | wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format); | ||
38 | exit(1); | ||
39 | } | ||
40 | char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); | ||
41 | if (map_shm == MAP_FAILED) { | ||
42 | close(fd); | ||
43 | wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting"); | ||
44 | exit(1); | ||
45 | } | ||
46 | struct xkb_keymap *keymap = xkb_keymap_new_from_string( | ||
47 | state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0); | ||
48 | munmap(map_shm, size); | ||
49 | close(fd); | ||
50 | assert(keymap); | ||
51 | struct xkb_state *xkb_state = xkb_state_new(keymap); | ||
52 | assert(xkb_state); | ||
53 | xkb_keymap_unref(state->xkb.keymap); | ||
54 | xkb_state_unref(state->xkb.state); | ||
55 | state->xkb.keymap = keymap; | ||
56 | state->xkb.state = xkb_state; | ||
57 | } | ||
58 | |||
59 | static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, | ||
60 | uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { | ||
61 | // Who cares | ||
62 | } | ||
63 | |||
64 | static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, | ||
65 | uint32_t serial, struct wl_surface *surface) { | ||
66 | // Who cares | ||
67 | } | ||
68 | |||
69 | static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, | ||
70 | uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { | ||
71 | struct swaylock_state *state = data; | ||
72 | enum wl_keyboard_key_state key_state = _key_state; | ||
73 | xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); | ||
74 | uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? | ||
75 | key + 8 : 0; | ||
76 | uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); | ||
77 | if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) { | ||
78 | swaylock_handle_key(state, sym, codepoint); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, | ||
83 | uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, | ||
84 | uint32_t mods_locked, uint32_t group) { | ||
85 | struct swaylock_state *state = data; | ||
86 | xkb_state_update_mask(state->xkb.state, | ||
87 | mods_depressed, mods_latched, mods_locked, 0, 0, group); | ||
88 | xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state, | ||
89 | XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); | ||
90 | state->xkb.modifiers = 0; | ||
91 | for (uint32_t i = 0; i < MASK_LAST; ++i) { | ||
92 | if (mask & state->xkb.masks[i]) { | ||
93 | state->xkb.modifiers |= XKB_MODS[i]; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, | ||
99 | int32_t rate, int32_t delay) { | ||
100 | // TODO | ||
101 | } | ||
102 | |||
103 | static const struct wl_keyboard_listener keyboard_listener = { | ||
104 | .keymap = keyboard_keymap, | ||
105 | .enter = keyboard_enter, | ||
106 | .leave = keyboard_leave, | ||
107 | .key = keyboard_key, | ||
108 | .modifiers = keyboard_modifiers, | ||
109 | .repeat_info = keyboard_repeat_info, | ||
110 | }; | ||
111 | |||
112 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | ||
113 | uint32_t serial, struct wl_surface *surface, | ||
114 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
115 | wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); | ||
116 | } | ||
117 | |||
118 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | ||
119 | uint32_t serial, struct wl_surface *surface) { | ||
120 | // Who cares | ||
121 | } | ||
122 | |||
123 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | ||
124 | uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
125 | // Who cares | ||
126 | } | ||
127 | |||
128 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | ||
129 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { | ||
130 | // Who cares | ||
131 | } | ||
132 | |||
133 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | ||
134 | uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
135 | // Who cares | ||
136 | } | ||
137 | |||
138 | static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { | ||
139 | // Who cares | ||
140 | } | ||
141 | |||
142 | static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, | ||
143 | uint32_t axis_source) { | ||
144 | // Who cares | ||
145 | } | ||
146 | |||
147 | static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, | ||
148 | uint32_t time, uint32_t axis) { | ||
149 | // Who cares | ||
150 | } | ||
151 | |||
152 | static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, | ||
153 | uint32_t axis, int32_t discrete) { | ||
154 | // Who cares | ||
155 | } | ||
156 | |||
157 | static const struct wl_pointer_listener pointer_listener = { | ||
158 | .enter = wl_pointer_enter, | ||
159 | .leave = wl_pointer_leave, | ||
160 | .motion = wl_pointer_motion, | ||
161 | .button = wl_pointer_button, | ||
162 | .axis = wl_pointer_axis, | ||
163 | .frame = wl_pointer_frame, | ||
164 | .axis_source = wl_pointer_axis_source, | ||
165 | .axis_stop = wl_pointer_axis_stop, | ||
166 | .axis_discrete = wl_pointer_axis_discrete, | ||
167 | }; | ||
168 | |||
169 | static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, | ||
170 | enum wl_seat_capability caps) { | ||
171 | struct swaylock_state *state = data; | ||
172 | if ((caps & WL_SEAT_CAPABILITY_POINTER)) { | ||
173 | struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat); | ||
174 | wl_pointer_add_listener(pointer, &pointer_listener, NULL); | ||
175 | } | ||
176 | if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { | ||
177 | struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); | ||
178 | wl_keyboard_add_listener(keyboard, &keyboard_listener, state); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | static void seat_handle_name(void *data, struct wl_seat *wl_seat, | ||
183 | const char *name) { | ||
184 | // Who cares | ||
185 | } | ||
186 | |||
187 | const struct wl_seat_listener seat_listener = { | ||
188 | .capabilities = seat_handle_capabilities, | ||
189 | .name = seat_handle_name, | ||
190 | }; | ||