aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Tony Crisci <tony@dubstepdish.com>2018-04-04 22:36:09 -0400
committerLibravatar Tony Crisci <tony@dubstepdish.com>2018-04-04 22:36:09 -0400
commit65f254f3fbc83d006d4ec29170ec8a8695345d6c (patch)
tree3044fb62120ca23499d31275076af50db09a9850
parentfix focus child (diff)
parentMerge pull request #1732 from emersion/view-children (diff)
downloadsway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.tar.gz
sway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.tar.zst
sway-65f254f3fbc83d006d4ec29170ec8a8695345d6c.zip
Merge branch 'wlroots' into fix-focus-inactive
-rw-r--r--common/background-image.c119
-rw-r--r--common/meson.build21
-rw-r--r--common/unicode.c101
-rw-r--r--include/background-image.h20
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/input/input-manager.h5
-rw-r--r--include/sway/input/seat.h8
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/sway/tree/view.h11
-rw-r--r--include/swaylock/seat.h38
-rw-r--r--include/swaylock/swaylock.h98
-rw-r--r--include/unicode.h33
-rw-r--r--meson.build2
-rw-r--r--protocols/meson.build6
-rw-r--r--protocols/wlr-input-inhibitor-unstable-v1.xml67
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/opacity.c39
-rw-r--r--sway/desktop/output.c41
-rw-r--r--sway/desktop/wl_shell.c4
-rw-r--r--sway/desktop/xdg_shell_v6.c4
-rw-r--r--sway/desktop/xwayland.c4
-rw-r--r--sway/input/cursor.c6
-rw-r--r--sway/input/input-manager.c35
-rw-r--r--sway/input/seat.c75
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway.5.txt4
-rw-r--r--sway/tree/container.c70
-rw-r--r--swaybg/main.c139
-rw-r--r--swaylock/main.c900
-rw-r--r--swaylock/meson.build23
-rw-r--r--swaylock/password.c126
-rw-r--r--swaylock/render.c150
-rw-r--r--swaylock/seat.c190
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
7enum 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
25cairo_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
56void 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 @@
1deps = [
2 cairo,
3 pango,
4 pangocairo,
5 wlroots
6]
7
8if gdk_pixbuf.found()
9 deps += [gdk_pixbuf]
10endif
11
12lib_sway_common = static_library( 1lib_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
5size_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
16static const uint8_t masks[] = {
17 0x7F,
18 0x1F,
19 0x0F,
20 0x07,
21 0x03,
22 0x01
23};
24
25uint32_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
51size_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
79static 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
93int 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
5enum 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
15enum background_mode parse_background_mode(const char *mode);
16cairo_surface_t *load_background_image(const char *path);
17void 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;
123sway_cmd cmd_mode; 123sway_cmd cmd_mode;
124sway_cmd cmd_mouse_warping; 124sway_cmd cmd_mouse_warping;
125sway_cmd cmd_move; 125sway_cmd cmd_move;
126sway_cmd cmd_opacity;
126sway_cmd cmd_new_float; 127sway_cmd cmd_new_float;
127sway_cmd cmd_new_window; 128sway_cmd cmd_new_window;
128sway_cmd cmd_no_focus; 129sway_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
29struct sway_input_manager *input_manager_create(struct sway_server *server); 34struct 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,
64void seat_set_focus_layer(struct sway_seat *seat, 67void seat_set_focus_layer(struct sway_seat *seat,
65 struct wlr_layer_surface *layer); 68 struct wlr_layer_surface *layer);
66 69
70void seat_set_exclusive_client(struct sway_seat *seat,
71 struct wl_client *client);
72
67struct sway_container *seat_get_focus(struct sway_seat *seat); 73struct 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
86struct seat_config *seat_get_config(struct sway_seat *seat); 92struct seat_config *seat_get_config(struct sway_seat *seat);
87 93
94bool 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
61enum sway_view_type { 61enum 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
69enum sway_view_prop { 67enum 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
109struct sway_view *view_create(enum sway_view_type type, 104struct 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
5enum 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
16enum 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
28struct 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
36extern 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>
6enum 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
14enum auth_state { 12enum 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
22enum line_source { 20struct swaylock_args {
23 LINE_SOURCE_DEFAULT,
24 LINE_SOURCE_RING,
25 LINE_SOURCE_INSIDE,
26};
27
28struct 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
41struct lock_colors { 26struct 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
46struct lock_config { 32struct 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; 46struct 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
64void render(struct render_data* render_data, struct lock_config *config); 59void swaylock_handle_key(struct swaylock_state *state,
60 xkb_keysym_t keysym, uint32_t codepoint);
61void render_frame(struct swaylock_surface *surface);
62void 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 */
15uint32_t utf8_decode(const char **str);
16
17/**
18 * Encodes a character as UTF-8 and returns the length of that character.
19 */
20size_t utf8_encode(char *str, uint32_t ch);
21
22/**
23 * Returns the size of the next UTF-8 character
24 */
25int utf8_size(const char *str);
26
27/**
28 * Returns the size of a UTF-8 character
29 */
30size_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)
35pixman = dependency('pixman-1') 35pixman = dependency('pixman-1')
36libcap = dependency('libcap') 36libcap = dependency('libcap')
37libinput = dependency('libinput') 37libinput = dependency('libinput')
38libpam = cc.find_library('libpam')
38math = cc.find_library('m') 39math = cc.find_library('m')
39rt = cc.find_library('rt') 40rt = cc.find_library('rt')
40git = find_program('git', required: false) 41git = find_program('git', required: false)
@@ -105,6 +106,7 @@ subdir('swaymsg')
105subdir('client') 106subdir('client')
106subdir('swaybg') 107subdir('swaybg')
107subdir('swaybar') 108subdir('swaybar')
109subdir('swaylock')
108 110
109config = configuration_data() 111config = configuration_data()
110config.set('sysconfdir', join_paths(prefix, sysconfdir)) 112config.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
23client_protocols = [ 23client_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
28server_protocols = [ 29server_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
33client_protos_src = [] 35client_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
7static 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
16struct 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
76static void render_surface(struct wlr_surface *surface, 76static 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
117static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, 117static 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
145static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, 145static 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
14static bool assert_wl_shell(struct sway_view *view) { 14static 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
14static bool assert_xdg(struct sway_view *view) { 14static 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
44static bool assert_xwayland(struct sway_view *view) { 44static 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
267static 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
276static 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
266struct sway_input_manager *input_manager_create( 293struct 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
356bool 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
353void seat_set_focus_warp(struct sway_seat *seat, 362void 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
425void seat_set_focus_layer(struct sway_seat *seat, 440void 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
478void 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
456struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, 523struct 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
15enum 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
24struct swaybg_args { 16struct 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
74static 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
140static void render_frame(struct swaybg_state *state) { 66static 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"
24struct registry *registry; 23#include "wlr-layer-shell-unstable-v1-client-protocol.h"
25struct render_data render_data; 24
26struct lock_config *config; 25static void daemonize() {
27bool show_indicator = true; 26 if (fork() == 0) {
28 27 int devnull = open("/dev/null", O_RDWR);
29void 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
37void sigalarm_handler(int sig) { 36static 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
46void sway_terminate(int exit_code) { 46static 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
59char *password; 54static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
60int password_size; 55 .configure = layer_surface_configure,
61enum line_source line_source = LINE_SOURCE_DEFAULT; 56 .closed = layer_surface_closed,
57};
62 58
63struct lock_config *init_config() { 59static 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
90void free_config(struct lock_config *config) { 65static 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
95int function_conversation(int num_msg, const struct pam_message **msg, 70static 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/** 74static 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;
133bool 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
152void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) { 82struct 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: 89static 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
248void render_color(struct window *window, uint32_t color) { 119static 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
253void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) { 124static 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) { 129static 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
323cairo_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
344int main(int argc, char **argv) { 131int 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, &registry_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
647void 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 @@
1executable(
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
12static 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
33static 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;
57fail:
58 // PAM frees this
59 pw->buffer = NULL;
60 pw->len = pw->size = 0;
61 return false;
62}
63
64static 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
72static 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
97void 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
10const int ARC_RADIUS = 50;
11const int ARC_THICKNESS = 10;
12const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
13const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
14
15void 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
145void 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
10const 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
21const 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
32static 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
59static 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
64static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
65 uint32_t serial, struct wl_surface *surface) {
66 // Who cares
67}
68
69static 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
82static 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
98static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
99 int32_t rate, int32_t delay) {
100 // TODO
101}
102
103static 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
112static 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
118static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
119 uint32_t serial, struct wl_surface *surface) {
120 // Who cares
121}
122
123static 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
128static 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
133static 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
138static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
139 // Who cares
140}
141
142static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
143 uint32_t axis_source) {
144 // Who cares
145}
146
147static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
148 uint32_t time, uint32_t axis) {
149 // Who cares
150}
151
152static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
153 uint32_t axis, int32_t discrete) {
154 // Who cares
155}
156
157static 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
169static 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
182static void seat_handle_name(void *data, struct wl_seat *wl_seat,
183 const char *name) {
184 // Who cares
185}
186
187const struct wl_seat_listener seat_listener = {
188 .capabilities = seat_handle_capabilities,
189 .name = seat_handle_name,
190};