aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <wlr/util/log.h>
+#include "background-image.h"
+#include "cairo.h"
+
+enum background_mode parse_background_mode(const char *mode) {
+ if (strcmp(mode, "stretch") == 0) {
+ return BACKGROUND_MODE_STRETCH;
+ } else if (strcmp(mode, "fill") == 0) {
+ return BACKGROUND_MODE_FILL;
+ } else if (strcmp(mode, "fit") == 0) {
+ return BACKGROUND_MODE_FIT;
+ } else if (strcmp(mode, "center") == 0) {
+ return BACKGROUND_MODE_CENTER;
+ } else if (strcmp(mode, "tile") == 0) {
+ return BACKGROUND_MODE_TILE;
+ } else if (strcmp(mode, "solid_color") == 0) {
+ return BACKGROUND_MODE_SOLID_COLOR;
+ }
+ wlr_log(L_ERROR, "Unsupported background mode: %s", mode);
+ return BACKGROUND_MODE_INVALID;
+}
+
+cairo_surface_t *load_background_image(const char *path) {
+ cairo_surface_t *image;
+#ifdef HAVE_GDK_PIXBUF
+ GError *err = NULL;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
+ if (!pixbuf) {
+ wlr_log(L_ERROR, "Failed to load background image (%s).",
+ err->message);
+ return false;
+ }
+ image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
+#else
+ image = cairo_image_surface_create_from_png(path);
+#endif //HAVE_GDK_PIXBUF
+ if (!image) {
+ wlr_log(L_ERROR, "Failed to read background image.");
+ return NULL;
+ }
+ if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
+ wlr_log(L_ERROR, "Failed to read background image: %s."
+#ifndef HAVE_GDK_PIXBUF
+ "\nSway was compiled without gdk_pixbuf support, so only"
+ "\nPNG images can be loaded. This is the likely cause."
+#endif //HAVE_GDK_PIXBUF
+ , cairo_status_to_string(cairo_surface_status(image)));
+ return NULL;
+ }
+ return image;
+}
+
+void render_background_image(cairo_t *cairo, cairo_surface_t *image,
+ enum background_mode mode, int buffer_width, int buffer_height) {
+ double width = cairo_image_surface_get_width(image);
+ double height = cairo_image_surface_get_height(image);
+
+ switch (mode) {
+ case BACKGROUND_MODE_STRETCH:
+ cairo_scale(cairo,
+ (double)buffer_width / width,
+ (double)buffer_height / height);
+ cairo_set_source_surface(cairo, image, 0, 0);
+ break;
+ case BACKGROUND_MODE_FILL: {
+ double window_ratio = (double)buffer_width / buffer_height;
+ double bg_ratio = width / height;
+
+ if (window_ratio > bg_ratio) {
+ double scale = (double)buffer_width / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)buffer_height / 2 / scale - height / 2);
+ } else {
+ double scale = (double)buffer_height / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 / scale - width / 2, 0);
+ }
+ break;
+ }
+ case BACKGROUND_MODE_FIT: {
+ double window_ratio = (double)buffer_width / buffer_height;
+ double bg_ratio = width / height;
+
+ if (window_ratio > bg_ratio) {
+ double scale = (double)buffer_height / height;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 / scale - width / 2, 0);
+ } else {
+ double scale = (double)buffer_width / width;
+ cairo_scale(cairo, scale, scale);
+ cairo_set_source_surface(cairo, image,
+ 0, (double)buffer_height / 2 / scale - height / 2);
+ }
+ break;
+ }
+ case BACKGROUND_MODE_CENTER:
+ cairo_set_source_surface(cairo, image,
+ (double)buffer_width / 2 - width / 2,
+ (double)buffer_height / 2 - height / 2);
+ break;
+ case BACKGROUND_MODE_TILE: {
+ cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
+ cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
+ cairo_set_source(cairo, pattern);
+ break;
+ }
+ case BACKGROUND_MODE_SOLID_COLOR:
+ case BACKGROUND_MODE_INVALID:
+ assert(0);
+ break;
+ }
+ cairo_paint(cairo);
+}
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 @@
-deps = [
- cairo,
- pango,
- pangocairo,
- wlroots
-]
-
-if gdk_pixbuf.found()
- deps += [gdk_pixbuf]
-endif
-
lib_sway_common = static_library(
'sway-common',
files(
+ 'background-image.c',
'cairo.c',
'ipc-client.c',
'log.c',
@@ -19,8 +9,15 @@ lib_sway_common = static_library(
'pango.c',
'readline.c',
'stringop.c',
+ 'unicode.c',
'util.c'
),
- dependencies: deps,
+ dependencies: [
+ cairo,
+ gdk_pixbuf,
+ pango,
+ pangocairo,
+ wlroots
+ ],
include_directories: sway_inc
)
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 @@
+#include <stdint.h>
+#include <stddef.h>
+#include "unicode.h"
+
+size_t utf8_chsize(uint32_t ch) {
+ if (ch < 0x80) {
+ return 1;
+ } else if (ch < 0x800) {
+ return 2;
+ } else if (ch < 0x10000) {
+ return 3;
+ }
+ return 4;
+}
+
+static const uint8_t masks[] = {
+ 0x7F,
+ 0x1F,
+ 0x0F,
+ 0x07,
+ 0x03,
+ 0x01
+};
+
+uint32_t utf8_decode(const char **char_str) {
+ uint8_t **s = (uint8_t **)char_str;
+
+ uint32_t cp = 0;
+ if (**s < 128) {
+ // shortcut
+ cp = **s;
+ ++*s;
+ return cp;
+ }
+ int size = utf8_size((char *)*s);
+ if (size == -1) {
+ ++*s;
+ return UTF8_INVALID;
+ }
+ uint8_t mask = masks[size - 1];
+ cp = **s & mask;
+ ++*s;
+ while (--size) {
+ cp <<= 6;
+ cp |= **s & 0x3f;
+ ++*s;
+ }
+ return cp;
+}
+
+size_t utf8_encode(char *str, uint32_t ch) {
+ size_t len = 0;
+ uint8_t first;
+
+ if (ch < 0x80) {
+ first = 0;
+ len = 1;
+ } else if (ch < 0x800) {
+ first = 0xc0;
+ len = 2;
+ } else if (ch < 0x10000) {
+ first = 0xe0;
+ len = 3;
+ } else {
+ first = 0xf0;
+ len = 4;
+ }
+
+ for (size_t i = len - 1; i > 0; --i) {
+ str[i] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ }
+
+ str[0] = ch | first;
+ return len;
+}
+
+
+static const struct {
+ uint8_t mask;
+ uint8_t result;
+ int octets;
+} sizes[] = {
+ { 0x80, 0x00, 1 },
+ { 0xE0, 0xC0, 2 },
+ { 0xF0, 0xE0, 3 },
+ { 0xF8, 0xF0, 4 },
+ { 0xFC, 0xF8, 5 },
+ { 0xFE, 0xF8, 6 },
+ { 0x80, 0x80, -1 },
+};
+
+int utf8_size(const char *s) {
+ uint8_t c = (uint8_t)*s;
+ for (size_t i = 0; i < sizeof(sizes) / 2; ++i) {
+ if ((c & sizes[i].mask) == sizes[i].result) {
+ return sizes[i].octets;
+ }
+ }
+ return -1;
+}
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 @@
+#ifndef _SWAY_BACKGROUND_IMAGE_H
+#define _SWAY_BACKGROUND_IMAGE_H
+#include "cairo.h"
+
+enum background_mode {
+ BACKGROUND_MODE_STRETCH,
+ BACKGROUND_MODE_FILL,
+ BACKGROUND_MODE_FIT,
+ BACKGROUND_MODE_CENTER,
+ BACKGROUND_MODE_TILE,
+ BACKGROUND_MODE_SOLID_COLOR,
+ BACKGROUND_MODE_INVALID,
+};
+
+enum background_mode parse_background_mode(const char *mode);
+cairo_surface_t *load_background_image(const char *path);
+void render_background_image(cairo_t *cairo, cairo_surface_t *image,
+ enum background_mode mode, int buffer_width, int buffer_height);
+
+#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;
sway_cmd cmd_mode;
sway_cmd cmd_mouse_warping;
sway_cmd cmd_move;
+sway_cmd cmd_opacity;
sway_cmd cmd_new_float;
sway_cmd cmd_new_window;
sway_cmd cmd_no_focus;
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 8e39a4a7..89a3ac71 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -1,6 +1,7 @@
#ifndef _SWAY_INPUT_INPUT_MANAGER_H
#define _SWAY_INPUT_INPUT_MANAGER_H
#include <libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
#include "sway/server.h"
#include "sway/config.h"
#include "list.h"
@@ -23,7 +24,11 @@ struct sway_input_manager {
struct wl_list devices;
struct wl_list seats;
+ struct wlr_input_inhibit_manager *inhibit;
+
struct wl_listener new_input;
+ struct wl_listener inhibit_activate;
+ struct wl_listener inhibit_deactivate;
};
struct sway_input_manager *input_manager_create(struct sway_server *server);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 137fcd22..d1cfbe4c 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -32,6 +32,9 @@ struct sway_seat {
// If the focused layer is set, views cannot receive keyboard focus
struct wlr_layer_surface *focused_layer;
+ // If exclusive_client is set, no other clients will receive input events
+ struct wl_client *exclusive_client;
+
struct wl_listener focus_destroy;
struct wl_listener new_container;
@@ -64,6 +67,9 @@ void seat_set_focus_warp(struct sway_seat *seat,
void seat_set_focus_layer(struct sway_seat *seat,
struct wlr_layer_surface *layer);
+void seat_set_exclusive_client(struct sway_seat *seat,
+ struct wl_client *client);
+
struct sway_container *seat_get_focus(struct sway_seat *seat);
/**
@@ -85,4 +91,6 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
struct seat_config *seat_get_config(struct sway_seat *seat);
+bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
+
#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 {
list_t *marks; // list of char*
+ float alpha;
+
struct {
struct wl_signal destroy;
// 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 {
};
enum sway_view_type {
- SWAY_WL_SHELL_VIEW,
- SWAY_XDG_SHELL_V6_VIEW,
- SWAY_XWAYLAND_VIEW,
- // Keep last
- SWAY_VIEW_TYPES,
+ SWAY_VIEW_WL_SHELL,
+ SWAY_VIEW_XDG_SHELL_V6,
+ SWAY_VIEW_XWAYLAND,
};
enum sway_view_prop {
@@ -101,9 +99,6 @@ struct sway_view {
struct sway_xwayland_surface *sway_xwayland_surface;
struct sway_wl_shell_surface *sway_wl_shell_surface;
};
-
- // only used for unmanaged views (shell specific)
- struct wl_list unmanaged_view_link; // sway_root::unmanaged_views
};
struct sway_view *view_create(enum sway_view_type type,
diff --git a/include/swaylock/seat.h b/include/swaylock/seat.h
new file mode 100644
index 00000000..44bc37d5
--- /dev/null
+++ b/include/swaylock/seat.h
@@ -0,0 +1,38 @@
+#ifndef _SWAYLOCK_SEAT_H
+#define _SWAYLOCK_SEAT_H
+#include <xkbcommon/xkbcommon.h>
+
+enum mod_bit {
+ MOD_SHIFT = 1<<0,
+ MOD_CAPS = 1<<1,
+ MOD_CTRL = 1<<2,
+ MOD_ALT = 1<<3,
+ MOD_MOD2 = 1<<4,
+ MOD_MOD3 = 1<<5,
+ MOD_LOGO = 1<<6,
+ MOD_MOD5 = 1<<7,
+};
+
+enum mask {
+ MASK_SHIFT,
+ MASK_CAPS,
+ MASK_CTRL,
+ MASK_ALT,
+ MASK_MOD2,
+ MASK_MOD3,
+ MASK_LOGO,
+ MASK_MOD5,
+ MASK_LAST
+};
+
+struct swaylock_xkb {
+ uint32_t modifiers;
+ struct xkb_state *state;
+ struct xkb_context *context;
+ struct xkb_keymap *keymap;
+ xkb_mod_mask_t masks[MASK_LAST];
+};
+
+extern const struct wl_seat_listener seat_listener;
+
+#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 @@
#ifndef _SWAYLOCK_H
#define _SWAYLOCK_H
-
-#include "client/cairo.h"
-
-enum scaling_mode {
- SCALING_MODE_STRETCH,
- SCALING_MODE_FILL,
- SCALING_MODE_FIT,
- SCALING_MODE_CENTER,
- SCALING_MODE_TILE,
-};
+#include <stdbool.h>
+#include <stdint.h>
+#include <wayland-client.h>
+#include "background-image.h"
+#include "cairo.h"
+#include "pool-buffer.h"
+#include "swaylock/seat.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
enum auth_state {
- AUTH_STATE_IDLE,
- AUTH_STATE_INPUT,
- AUTH_STATE_BACKSPACE,
- AUTH_STATE_VALIDATING,
- AUTH_STATE_INVALID,
+ AUTH_STATE_IDLE,
+ AUTH_STATE_INPUT,
+ AUTH_STATE_BACKSPACE,
+ AUTH_STATE_VALIDATING,
+ AUTH_STATE_INVALID,
};
-enum line_source {
- LINE_SOURCE_DEFAULT,
- LINE_SOURCE_RING,
- LINE_SOURCE_INSIDE,
-};
-
-struct render_data {
- list_t *surfaces;
- // Output specific images
- cairo_surface_t **images;
- // OR one image for all outputs:
- cairo_surface_t *image;
- int num_images;
- int color_set;
+struct swaylock_args {
uint32_t color;
- enum scaling_mode scaling_mode;
- enum auth_state auth_state;
+ enum background_mode mode;
+ bool show_indicator;
};
-struct lock_colors {
- uint32_t inner_ring;
- uint32_t outer_ring;
+struct swaylock_password {
+ size_t size;
+ size_t len;
+ char *buffer;
};
-struct lock_config {
- char *font;
-
- struct {
- uint32_t text;
- uint32_t line;
- uint32_t separator;
- uint32_t input_cursor;
- uint32_t backspace_cursor;
- struct lock_colors normal;
- struct lock_colors validating;
- struct lock_colors invalid;
- } colors;
+struct swaylock_state {
+ struct wl_display *display;
+ struct wl_compositor *compositor;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
+ struct wl_shm *shm;
+ struct wl_list surfaces;
+ struct swaylock_args args;
+ struct swaylock_password password;
+ struct swaylock_xkb xkb;
+ enum auth_state auth_state;
+ bool run_display;
+};
- int radius;
- int thickness;
+struct swaylock_surface {
+ cairo_surface_t *image;
+ struct swaylock_state *state;
+ struct wl_output *output;
+ struct wl_surface *surface;
+ struct zwlr_layer_surface_v1 *layer_surface;
+ struct pool_buffer buffers[2];
+ struct pool_buffer *current_buffer;
+ uint32_t width, height;
+ int32_t scale;
+ struct wl_list link;
};
-void render(struct render_data* render_data, struct lock_config *config);
+void swaylock_handle_key(struct swaylock_state *state,
+ xkb_keysym_t keysym, uint32_t codepoint);
+void render_frame(struct swaylock_surface *surface);
+void render_frames(struct swaylock_state *state);
#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 @@
+#ifndef _SWAY_UNICODE_H
+#define _SWAY_UNICODE_H
+#include <stddef.h>
+#include <stdint.h>
+
+// Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself
+// doesn't really bother with more than 4.
+#define UTF8_MAX_SIZE 4
+
+#define UTF8_INVALID 0x80
+
+/**
+ * Grabs the next UTF-8 character and advances the string pointer
+ */
+uint32_t utf8_decode(const char **str);
+
+/**
+ * Encodes a character as UTF-8 and returns the length of that character.
+ */
+size_t utf8_encode(char *str, uint32_t ch);
+
+/**
+ * Returns the size of the next UTF-8 character
+ */
+int utf8_size(const char *str);
+
+/**
+ * Returns the size of a UTF-8 character
+ */
+size_t utf8_chsize(uint32_t ch);
+
+#endif
+
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)
pixman = dependency('pixman-1')
libcap = dependency('libcap')
libinput = dependency('libinput')
+libpam = cc.find_library('libpam')
math = cc.find_library('m')
rt = cc.find_library('rt')
git = find_program('git', required: false)
@@ -105,6 +106,7 @@ subdir('swaymsg')
subdir('client')
subdir('swaybg')
subdir('swaybar')
+subdir('swaylock')
config = configuration_data()
config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/protocols/meson.build b/protocols/meson.build
index 0887cf86..7f83b16b 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -22,12 +22,14 @@ wayland_scanner_server = generator(
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
- ['wlr-layer-shell-unstable-v1.xml']
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-input-inhibitor-unstable-v1.xml']
]
server_protocols = [
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
- ['wlr-layer-shell-unstable-v1.xml']
+ ['wlr-layer-shell-unstable-v1.xml'],
+ ['wlr-input-inhibitor-unstable-v1.xml']
]
client_protos_src = []
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml
new file mode 100644
index 00000000..b62d1bb4
--- /dev/null
+++ b/protocols/wlr-input-inhibitor-unstable-v1.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_input_inhibit_unstable_v1">
+ <copyright>
+ Copyright © 2018 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_input_inhibit_manager_v1" version="1">
+ <description summary="inhibits input events to other clients">
+ Clients can use this interface to prevent input events from being sent to
+ any surfaces but its own, which is useful for example in lock screen
+ software. It is assumed that access to this interface will be locked down
+ to whitelisted clients by the compositor.
+ </description>
+
+ <request name="get_inhibitor">
+ <description summary="inhibit input to other clients">
+ Activates the input inhibitor. As long as the inhibitor is active, the
+ compositor will not send input events to other clients.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
+ </request>
+
+ <enum name="error">
+ <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
+ </enum>
+ </interface>
+
+ <interface name="zwlr_input_inhibitor_v1" version="1">
+ <description summary="inhibits input to other clients">
+ While this resource exists, input to clients other than the owner of the
+ inhibitor resource will not receive input events. The client that owns
+ this resource will receive all input events normally. The compositor will
+ also disable all of its own input processing (such as keyboard shortcuts)
+ while the inhibitor is active.
+
+ The compositor may continue to send input events to selected clients,
+ such as an on-screen keyboard (via the input-method protocol).
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the input inhibitor object">
+ Destroy the inhibitor and allow other clients to receive input.
+ </description>
+ </request>
+ </interface>
+</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[] = {
{ "kill", cmd_kill },
{ "layout", cmd_layout },
{ "move", cmd_move },
+ { "opacity", cmd_opacity },
{ "reload", cmd_reload },
{ "split", cmd_split },
{ "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 @@
+#include <assert.h>
+#include <stdlib.h>
+#include "sway/commands.h"
+#include "sway/tree/view.h"
+#include "log.h"
+
+static bool parse_opacity(const char *opacity, float *val) {
+ char *err;
+ *val = strtof(opacity, &err);
+ if (*val < 0 || *val > 1 || *err) {
+ return false;
+ }
+ return true;
+}
+
+struct cmd_results *cmd_opacity(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+
+ struct sway_container *con =
+ config->handler_context.current_container;
+
+ float opacity = 0.0f;
+
+ if (!parse_opacity(argv[0], &opacity)) {
+ return cmd_results_new(CMD_INVALID, "opacity <value>",
+ "Invalid value (expected 0..1): %s", argv[0]);
+ }
+
+ con->alpha = opacity;
+
+ if (con->type == C_VIEW) {
+ view_damage_whole(con->sway_view);
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
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,
static void render_surface(struct wlr_surface *surface,
struct wlr_output *wlr_output, struct timespec *when,
- double ox, double oy, float rotation) {
+ double ox, double oy, float rotation, float alpha) {
struct wlr_renderer *renderer =
wlr_backend_get_renderer(wlr_output->backend);
@@ -95,8 +95,8 @@ static void render_surface(struct wlr_surface *surface,
wlr_matrix_project_box(matrix, &box, transform, rotation,
wlr_output->transform_matrix);
- // TODO: configurable alpha
- wlr_render_texture_with_matrix(renderer, surface->texture, matrix, 1.0f);
+ wlr_render_texture_with_matrix(renderer, surface->texture,
+ matrix, alpha);
wlr_surface_send_frame_done(surface, when);
}
@@ -110,13 +110,13 @@ static void render_surface(struct wlr_surface *surface,
surface->current->width, surface->current->height, rotation);
render_surface(subsurface->surface, wlr_output, when,
- ox + sx, oy + sy, rotation);
+ ox + sx, oy + sy, rotation, alpha);
}
}
static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
struct wlr_output *wlr_output, struct timespec *when, double base_x,
- double base_y, float rotation) {
+ double base_y, float rotation, float alpha) {
double width = surface->surface->current->width;
double height = surface->surface->current->height;
@@ -136,19 +136,19 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
width, height, rotation);
render_surface(popup->surface, wlr_output, when,
- base_x + popup_sx, base_y + popup_sy, rotation);
+ base_x + popup_sx, base_y + popup_sy, rotation, alpha);
render_xdg_v6_popups(popup, wlr_output, when,
- base_x + popup_sx, base_y + popup_sy, rotation);
+ base_x + popup_sx, base_y + popup_sy, rotation, alpha);
}
}
static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface,
struct wlr_output *wlr_output, struct timespec *when,
- double lx, double ly, float rotation,
+ double lx, double ly, float rotation, float alpha,
bool is_child) {
if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) {
render_surface(surface->surface, wlr_output, when,
- lx, ly, rotation);
+ lx, ly, rotation, alpha);
double width = surface->surface->current->width;
double height = surface->surface->current->height;
@@ -164,7 +164,7 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface,
width, height, rotation);
render_wl_shell_surface(popup, wlr_output, when,
- lx + popup_x, ly + popup_y, rotation, true);
+ lx + popup_x, ly + popup_y, rotation, alpha, true);
}
}
}
@@ -181,29 +181,28 @@ static void render_view(struct sway_container *view, void *data) {
struct wlr_output *wlr_output = output->wlr_output;
struct sway_view *sway_view = view->sway_view;
struct wlr_surface *surface = sway_view->surface;
+ float alpha = sway_view->swayc->alpha;
if (!surface) {
return;
}
switch (sway_view->type) {
- case SWAY_XDG_SHELL_V6_VIEW: {
+ case SWAY_VIEW_XDG_SHELL_V6: {
int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x;
int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y;
render_surface(surface, wlr_output, when,
- view->x - window_offset_x, view->y - window_offset_y, 0);
+ view->x - window_offset_x, view->y - window_offset_y, 0, alpha);
render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output,
- when, view->x - window_offset_x, view->y - window_offset_y, 0);
+ when, view->x - window_offset_x, view->y - window_offset_y, 0, alpha);
break;
}
- case SWAY_WL_SHELL_VIEW:
+ case SWAY_VIEW_WL_SHELL:
render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output,
- when, view->x, view->y, 0, false);
+ when, view->x, view->y, 0, alpha, false);
break;
- case SWAY_XWAYLAND_VIEW:
- render_surface(surface, wlr_output, when, view->x, view->y, 0);
- break;
- default:
+ case SWAY_VIEW_XWAYLAND:
+ render_surface(surface, wlr_output, when, view->x, view->y, 0, alpha);
break;
}
}
@@ -214,7 +213,7 @@ static void render_layer(struct sway_output *output, struct timespec *when,
wl_list_for_each(sway_layer, layer, link) {
struct wlr_layer_surface *layer = sway_layer->layer_surface;
render_surface(layer->surface, output->wlr_output, when,
- sway_layer->geo.x, sway_layer->geo.y, 0);
+ sway_layer->geo.x, sway_layer->geo.y, 0, 1.0f);
wlr_surface_send_frame_done(layer->surface, when);
}
}
@@ -288,7 +287,7 @@ static void render_output(struct sway_output *output, struct timespec *when,
}
render_surface(xsurface->surface, wlr_output, &output->last_frame,
- view_box.x - output_box->x, view_box.y - output_box->y, 0);
+ view_box.x - output_box->x, view_box.y - output_box->y, 0, 1.0f);
}
// 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 @@
#include "log.h"
static bool assert_wl_shell(struct sway_view *view) {
- return sway_assert(view->type == SWAY_WL_SHELL_VIEW,
+ return sway_assert(view->type == SWAY_VIEW_WL_SHELL,
"Expecting wl_shell view!");
}
@@ -97,7 +97,7 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
return;
}
- struct sway_view *view = view_create(SWAY_WL_SHELL_VIEW, &view_impl);
+ struct sway_view *view = view_create(SWAY_VIEW_WL_SHELL, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) {
return;
}
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 @@
#include "log.h"
static bool assert_xdg(struct sway_view *view) {
- return sway_assert(view->type == SWAY_XDG_SHELL_V6_VIEW,
+ return sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
"Expected xdg shell v6 view!");
}
@@ -126,7 +126,7 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
return;
}
- struct sway_view *view = view_create(SWAY_XDG_SHELL_V6_VIEW, &view_impl);
+ struct sway_view *view = view_create(SWAY_VIEW_XDG_SHELL_V6, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) {
return;
}
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) {
static bool assert_xwayland(struct sway_view *view) {
- return sway_assert(view->type == SWAY_XWAYLAND_VIEW,
+ return sway_assert(view->type == SWAY_VIEW_XWAYLAND,
"Expected xwayland view!");
}
@@ -185,7 +185,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
return;
}
- struct sway_view *view = view_create(SWAY_XWAYLAND_VIEW, &view_impl);
+ struct sway_view *view = view_create(SWAY_VIEW_XWAYLAND, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) {
return;
}
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,
// send pointer enter/leave
if (surface != NULL) {
- wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
- wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ if (seat_is_input_allowed(cursor->seat, surface)) {
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ }
} else {
wlr_seat_pointer_clear_focus(seat);
}
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 @@
#include <libinput.h>
#include <math.h>
#include <wlr/backend/libinput.h>
+#include <wlr/types/wlr_input_inhibitor.h>
#include "sway/config.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
@@ -263,6 +264,32 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
input_device->device_destroy.notify = handle_device_destroy;
}
+static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_activate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, input_manager->inhibit->active_client);
+ }
+}
+
+static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) {
+ struct sway_input_manager *input_manager = wl_container_of(
+ listener, input_manager, inhibit_deactivate);
+ struct sway_seat *seat;
+ wl_list_for_each(seat, &input_manager->seats, link) {
+ seat_set_exclusive_client(seat, NULL);
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
+ }
+}
+
struct sway_input_manager *input_manager_create(
struct sway_server *server) {
struct sway_input_manager *input =
@@ -281,6 +308,14 @@ struct sway_input_manager *input_manager_create(
input->new_input.notify = handle_new_input;
wl_signal_add(&server->backend->events.new_input, &input->new_input);
+ input->inhibit = wlr_input_inhibit_manager_create(server->wl_display);
+ input->inhibit_activate.notify = handle_inhibit_activate;
+ wl_signal_add(&input->inhibit->events.activate,
+ &input->inhibit_activate);
+ input->inhibit_deactivate.notify = handle_inhibit_deactivate;
+ wl_signal_add(&input->inhibit->events.deactivate,
+ &input->inhibit_deactivate);
+
return input;
}
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 @@
#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 199309L
#include <assert.h>
+#include <time.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_xcursor_manager.h>
@@ -9,6 +11,7 @@
#include "sway/input/input-manager.h"
#include "sway/input/keyboard.h"
#include "sway/ipc-server.h"
+#include "sway/layers.h"
#include "sway/output.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
@@ -63,7 +66,7 @@ static void seat_send_focus(struct sway_seat *seat,
return;
}
struct sway_view *view = con->sway_view;
- if (view->type == SWAY_XWAYLAND_VIEW) {
+ if (view->type == SWAY_VIEW_XWAYLAND) {
struct wlr_xwayland *xwayland =
seat->input->server->xwayland;
wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -350,6 +353,12 @@ void seat_configure_xcursor(struct sway_seat *seat) {
seat->cursor->cursor->y);
}
+bool seat_is_input_allowed(struct sway_seat *seat,
+ struct wlr_surface *surface) {
+ struct wl_client *client = wl_resource_get_client(surface->resource);
+ return !seat->exclusive_client || seat->exclusive_client == client;
+}
+
void seat_set_focus_warp(struct sway_seat *seat,
struct sway_container *container, bool warp) {
if (seat->focused_layer) {
@@ -371,6 +380,12 @@ void seat_set_focus_warp(struct sway_seat *seat,
wl_list_remove(&seat_con->link);
wl_list_insert(&seat->focus_stack, &seat_con->link);
+ if (container->type == C_VIEW && !seat_is_input_allowed(
+ seat, container->sway_view->surface)) {
+ wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited");
+ return;
+ }
+
if (container->type == C_VIEW) {
seat_send_focus(seat, container);
}
@@ -424,11 +439,18 @@ void seat_set_focus(struct sway_seat *seat,
void seat_set_focus_layer(struct sway_seat *seat,
struct wlr_layer_surface *layer) {
- if (!layer) {
+ if (!layer && seat->focused_layer) {
seat->focused_layer = NULL;
+ struct sway_container *previous = seat_get_focus(seat);
+ if (previous) {
+ wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous,
+ container_type_to_str(previous->type), previous->name);
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
+ }
return;
- }
- if (seat->focused_layer == layer) {
+ } else if (!layer || seat->focused_layer == layer) {
return;
}
if (seat->has_focus) {
@@ -453,6 +475,51 @@ void seat_set_focus_layer(struct sway_seat *seat,
}
}
+void seat_set_exclusive_client(struct sway_seat *seat,
+ struct wl_client *client) {
+ if (!client) {
+ seat->exclusive_client = client;
+ // Triggers a refocus of the topmost surface layer if necessary
+ // TODO: Make layer surface focus per-output based on cursor position
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *output = root_container.children->items[i];
+ if (!sway_assert(output->type == C_OUTPUT,
+ "root container has non-output child")) {
+ continue;
+ }
+ arrange_layers(output->sway_output);
+ }
+ return;
+ }
+ if (seat->focused_layer) {
+ if (wl_resource_get_client(seat->focused_layer->resource) != client) {
+ seat_set_focus_layer(seat, NULL);
+ }
+ }
+ if (seat->has_focus) {
+ struct sway_container *focus = seat_get_focus(seat);
+ if (focus->type == C_VIEW && wl_resource_get_client(
+ focus->sway_view->surface->resource) != client) {
+ seat_set_focus(seat, NULL);
+ }
+ }
+ if (seat->wlr_seat->pointer_state.focused_client) {
+ if (seat->wlr_seat->pointer_state.focused_client->client != client) {
+ wlr_seat_pointer_clear_focus(seat->wlr_seat);
+ }
+ }
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ struct wlr_touch_point *point;
+ wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) {
+ if (point->client->client != client) {
+ wlr_seat_touch_point_clear_focus(seat->wlr_seat,
+ now.tv_nsec / 1000, point->touch_id);
+ }
+ }
+ seat->exclusive_client = client;
+}
+
struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_container *container) {
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(
'commands/focus.c',
'commands/focus_follows_mouse.c',
'commands/kill.c',
+ 'commands/opacity.c',
'commands/include.c',
'commands/input.c',
'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:
However, any mark that starts with an underscore will not be drawn even if the
option is on. The default option is _on_.
+**opacity** <value>::
+ Set the opacity of the window between 0 (completely transparent) and 1
+ (completely opaque).
+
**unmark** <identifier>::
**Unmark** will remove _identifier_ from the list of current marks on a window. If
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) {
c->layout = L_NONE;
c->workspace_layout = L_NONE;
c->type = type;
+ c->alpha = 1.0f;
+
if (type != C_VIEW) {
c->children = create_list();
}
@@ -416,51 +418,33 @@ struct sway_container *container_at(struct sway_container *parent,
double view_sx = ox - swayc->x;
double view_sy = oy - swayc->y;
+ double _sx, _sy;
+ struct wlr_surface *_surface;
switch (sview->type) {
- case SWAY_WL_SHELL_VIEW:
- break;
- case SWAY_XDG_SHELL_V6_VIEW:
- // the top left corner of the sway container is the
- // coordinate of the top left corner of the window geometry
- view_sx += sview->wlr_xdg_surface_v6->geometry.x;
- view_sy += sview->wlr_xdg_surface_v6->geometry.y;
-
- // check for popups
- double popup_sx, popup_sy;
- struct wlr_xdg_surface_v6 *popup =
- wlr_xdg_surface_v6_popup_at(sview->wlr_xdg_surface_v6,
- view_sx, view_sy, &popup_sx, &popup_sy);
-
- if (popup) {
- *sx = view_sx - popup_sx;
- *sy = view_sy - popup_sy;
- *surface = popup->surface;
- return swayc;
- }
- break;
- case SWAY_XWAYLAND_VIEW:
- break;
- default:
- break;
- }
-
- // check for subsurfaces
- double sub_x, sub_y;
- struct wlr_subsurface *subsurface =
- wlr_surface_subsurface_at(sview->surface,
- view_sx, view_sy, &sub_x, &sub_y);
- if (subsurface) {
- *sx = view_sx - sub_x;
- *sy = view_sy - sub_y;
- *surface = subsurface->surface;
- return swayc;
+ case SWAY_VIEW_XWAYLAND:
+ _surface = wlr_surface_surface_at(sview->surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_WL_SHELL:
+ _surface = wlr_wl_shell_surface_surface_at(
+ sview->wlr_wl_shell_surface,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
+ case SWAY_VIEW_XDG_SHELL_V6:
+ // the top left corner of the sway container is the
+ // coordinate of the top left corner of the window geometry
+ view_sx += sview->wlr_xdg_surface_v6->geometry.x;
+ view_sy += sview->wlr_xdg_surface_v6->geometry.y;
+
+ _surface = wlr_xdg_surface_v6_surface_at(
+ sview->wlr_xdg_surface_v6,
+ view_sx, view_sy, &_sx, &_sy);
+ break;
}
-
- if (wlr_surface_point_accepts_input(
- sview->surface, view_sx, view_sy)) {
- *sx = view_sx;
- *sy = view_sy;
- *surface = swayc->sway_view->surface;
+ if (_surface) {
+ *sx = _sx;
+ *sy = _sy;
+ *surface = _surface;
return swayc;
}
} 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 @@
#include <time.h>
#include <wayland-client.h>
#include <wlr/util/log.h>
+#include "background-image.h"
#include "pool-buffer.h"
#include "cairo.h"
#include "util.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
-enum background_mode {
- BACKGROUND_MODE_STRETCH,
- BACKGROUND_MODE_FILL,
- BACKGROUND_MODE_FIT,
- BACKGROUND_MODE_CENTER,
- BACKGROUND_MODE_TILE,
- BACKGROUND_MODE_SOLID_COLOR,
-};
-
struct swaybg_args {
int output_idx;
const char *path;
@@ -71,85 +63,18 @@ bool is_valid_color(const char *color) {
return true;
}
-static void render_image(struct swaybg_state *state) {
- cairo_t *cairo = state->current_buffer->cairo;
- cairo_surface_t *image = state->context.image;
- double width = cairo_image_surface_get_width(image);
- double height = cairo_image_surface_get_height(image);
- int buffer_width = state->width * state->scale;
- int buffer_height = state->height * state->scale;
-
- switch (state->args->mode) {
- case BACKGROUND_MODE_STRETCH:
- cairo_scale(cairo, (double)buffer_width / width,
- (double)buffer_height / height);
- cairo_set_source_surface(cairo, image, 0, 0);
- break;
- case BACKGROUND_MODE_FILL: {
- double window_ratio = (double)buffer_width / buffer_height;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double)buffer_width / width;
- cairo_scale(cairo, scale, scale);
- cairo_set_source_surface(cairo, image,
- 0, (double)buffer_height / 2 / scale - height / 2);
- } else {
- double scale = (double)buffer_height / height;
- cairo_scale(cairo, scale, scale);
- cairo_set_source_surface(cairo, image,
- (double)buffer_width / 2 / scale - width / 2, 0);
- }
- break;
- }
- case BACKGROUND_MODE_FIT: {
- double window_ratio = (double)buffer_width / buffer_height;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double)buffer_height / height;
- cairo_scale(cairo, scale, scale);
- cairo_set_source_surface(cairo, image,
- (double)buffer_width / 2 / scale - width / 2, 0);
- } else {
- double scale = (double)buffer_width / width;
- cairo_scale(cairo, scale, scale);
- cairo_set_source_surface(cairo, image,
- 0, (double)buffer_height / 2 / scale - height / 2);
- }
- break;
- }
- case BACKGROUND_MODE_CENTER:
- cairo_set_source_surface(cairo, image,
- (double)buffer_width / 2 - width / 2,
- (double)buffer_height / 2 - height / 2);
- break;
- case BACKGROUND_MODE_TILE: {
- cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
- cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
- cairo_set_source(cairo, pattern);
- break;
- }
- case BACKGROUND_MODE_SOLID_COLOR:
- assert(0);
- break;
- }
- cairo_paint(cairo);
-}
-
static void render_frame(struct swaybg_state *state) {
- state->current_buffer = get_next_buffer(state->shm, state->buffers,
- state->width * state->scale, state->height * state->scale);
+ int buffer_width = state->width * state->scale,
+ buffer_height = state->height * state->scale;
+ state->current_buffer = get_next_buffer(state->shm,
+ state->buffers, buffer_width, buffer_height);
cairo_t *cairo = state->current_buffer->cairo;
-
- switch (state->args->mode) {
- case BACKGROUND_MODE_SOLID_COLOR:
+ if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
cairo_set_source_u32(cairo, state->context.color);
cairo_paint(cairo);
- break;
- default:
- render_image(state);
- break;
+ } else {
+ render_background_image(cairo, state->context.image,
+ state->args->mode, buffer_width, buffer_height);
}
wl_surface_set_buffer_scale(state->surface, state->scale);
@@ -163,31 +88,7 @@ static bool prepare_context(struct swaybg_state *state) {
state->context.color = parse_color(state->args->path);
return is_valid_color(state->args->path);
}
-#ifdef HAVE_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err);
- if (!pixbuf) {
- wlr_log(L_ERROR, "Failed to load background image.");
- return false;
- }
- state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- state->context.image = cairo_image_surface_create_from_png(
- state->args->path);
-#endif //HAVE_GDK_PIXBUF
- if (!state->context.image) {
- wlr_log(L_ERROR, "Failed to read background image.");
- return false;
- }
- if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) {
- wlr_log(L_ERROR, "Failed to read background image: %s."
-#ifndef HAVE_GDK_PIXBUF
- "\nSway was compiled without gdk_pixbuf support, so only"
- "\nPNG images can be loaded. This is the likely cause."
-#endif //HAVE_GDK_PIXBUF
- , cairo_status_to_string(
- cairo_surface_status(state->context.image)));
+ if (!(state->context.image = load_background_image(state->args->path))) {
return false;
}
return true;
@@ -294,24 +195,10 @@ int main(int argc, const char **argv) {
args.output_idx = atoi(argv[1]);
args.path = argv[2];
- args.mode = BACKGROUND_MODE_STRETCH;
- if (strcmp(argv[3], "stretch") == 0) {
- args.mode = BACKGROUND_MODE_STRETCH;
- } else if (strcmp(argv[3], "fill") == 0) {
- args.mode = BACKGROUND_MODE_FILL;
- } else if (strcmp(argv[3], "fit") == 0) {
- args.mode = BACKGROUND_MODE_FIT;
- } else if (strcmp(argv[3], "center") == 0) {
- args.mode = BACKGROUND_MODE_CENTER;
- } else if (strcmp(argv[3], "tile") == 0) {
- args.mode = BACKGROUND_MODE_TILE;
- } else if (strcmp(argv[3], "solid_color") == 0) {
- args.mode = BACKGROUND_MODE_SOLID_COLOR;
- } else {
- wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]);
+ args.mode = parse_background_mode(argv[3]);
+ if (args.mode == BACKGROUND_MODE_INVALID) {
return 1;
}
-
if (!prepare_context(&state)) {
return 1;
}
@@ -345,10 +232,10 @@ int main(int argc, const char **argv) {
zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1);
zwlr_layer_surface_v1_add_listener(state.layer_surface,
&layer_surface_listener, &state);
- state.run_display = true;
wl_surface_commit(state.surface);
wl_display_roundtrip(state.display);
+ state.run_display = true;
while (wl_display_dispatch(state.display) != -1 && state.run_display) {
// This space intentionally left blank
}
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 @@
-#define _XOPEN_SOURCE 500
-#include "wayland-swaylock-client-protocol.h"
-#include <xkbcommon/xkbcommon.h>
-#include <xkbcommon/xkbcommon-names.h>
-#include <security/pam_appl.h>
-#include <json-c/json.h>
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdbool.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
-#include "client/window.h"
-#include "client/registry.h"
-#include "client/cairo.h"
+#include <wayland-client.h>
+#include <wlr/util/log.h>
+#include "swaylock/seat.h"
#include "swaylock/swaylock.h"
-#include "ipc-client.h"
-#include "log.h"
+#include "background-image.h"
+#include "pool-buffer.h"
+#include "cairo.h"
#include "util.h"
-
-struct registry *registry;
-struct render_data render_data;
-struct lock_config *config;
-bool show_indicator = true;
-
-void wl_dispatch_events() {
- wl_display_flush(registry->display);
- if (wl_display_dispatch(registry->display) == -1) {
- sway_log(L_ERROR, "failed to run wl_display_dispatch");
- exit(1);
+#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+
+static void daemonize() {
+ if (fork() == 0) {
+ int devnull = open("/dev/null", O_RDWR);
+ dup2(STDOUT_FILENO, devnull);
+ dup2(STDERR_FILENO, devnull);
+ chdir("/");
+ } else {
+ exit(0);
}
}
-void sigalarm_handler(int sig) {
- signal(SIGALRM, SIG_IGN);
- // Hide typing indicator
- render_data.auth_state = AUTH_STATE_IDLE;
- render(&render_data, config);
- wl_display_flush(registry->display);
- signal(SIGALRM, sigalarm_handler);
+static void layer_surface_configure(void *data,
+ struct zwlr_layer_surface_v1 *layer_surface,
+ uint32_t serial, uint32_t width, uint32_t height) {
+ struct swaylock_surface *surface = data;
+ surface->width = width;
+ surface->height = height;
+ zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
+ render_frame(surface);
}
-void sway_terminate(int exit_code) {
- int i;
- for (i = 0; i < render_data.surfaces->length; ++i) {
- struct window *window = render_data.surfaces->items[i];
- window_teardown(window);
- }
- list_free(render_data.surfaces);
- if (registry) {
- registry_teardown(registry);
- }
- exit(exit_code);
+static void layer_surface_closed(void *data,
+ struct zwlr_layer_surface_v1 *layer_surface) {
+ struct swaylock_surface *surface = data;
+ zwlr_layer_surface_v1_destroy(surface->layer_surface);
+ wl_surface_destroy(surface->surface);
+ surface->state->run_display = false;
}
-char *password;
-int password_size;
-enum line_source line_source = LINE_SOURCE_DEFAULT;
+static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = layer_surface_configure,
+ .closed = layer_surface_closed,
+};
-struct lock_config *init_config() {
- struct lock_config *config = calloc(1, sizeof(struct lock_config));
-
- config->font = strdup("sans-serif");
- config->colors.text = 0x000000FF;
-
- config->colors.line = 0x000000FF;
- config->colors.separator = 0x000000FF;
-
- config->colors.input_cursor = 0x33DB00FF;
- config->colors.backspace_cursor = 0xDB3300FF;
-
- config->colors.normal.inner_ring = 0x000000BF;
- config->colors.normal.outer_ring = 0x337D00FF;
-
- config->colors.validating.inner_ring = 0x0072FFBF;
- config->colors.validating.outer_ring = 0x3300FAFF;
-
- config->colors.invalid.inner_ring = 0xFA0000BF;
- config->colors.invalid.outer_ring = 0x7D3300FF;
-
- config->radius = 50;
- config->thickness = 10;
-
- return config;
+static void output_geometry(void *data, struct wl_output *output, int32_t x,
+ int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
+ const char *make, const char *model, int32_t transform) {
+ // Who cares
}
-void free_config(struct lock_config *config) {
- free(config->font);
- free(config);
+static void output_mode(void *data, struct wl_output *output, uint32_t flags,
+ int32_t width, int32_t height, int32_t refresh) {
+ // Who cares
}
-int function_conversation(int num_msg, const struct pam_message **msg,
- struct pam_response **resp, void *appdata_ptr) {
-
- const char* msg_style_names[] = {
- NULL,
- "PAM_PROMPT_ECHO_OFF",
- "PAM_PROMPT_ECHO_ON",
- "PAM_ERROR_MSG",
- "PAM_TEXT_INFO",
- };
-
- /* PAM expects an array of responses, one for each message */
- struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
- *resp = pam_reply;
-
- for(int i=0; i<num_msg; ++i) {
- sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
- msg_style_names[msg[i]->msg_style],
- msg[i]->msg);
-
- switch (msg[i]->msg_style) {
- case PAM_PROMPT_ECHO_OFF:
- case PAM_PROMPT_ECHO_ON:
- pam_reply[i].resp = password;
- break;
-
- case PAM_ERROR_MSG:
- case PAM_TEXT_INFO:
- break;
- }
- }
-
- return PAM_SUCCESS;
+static void output_done(void *data, struct wl_output *output) {
+ // Who cares
}
-/**
- * Note: PAM will free() 'password' during the process
- */
-bool verify_password() {
- struct passwd *passwd = getpwuid(getuid());
- char *username = passwd->pw_name;
-
- const struct pam_conv local_conversation = { function_conversation, NULL };
- pam_handle_t *local_auth_handle = NULL;
- int pam_err;
- if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
- sway_abort("PAM returned %d\n", pam_err);
- }
- if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
- return false;
+static void output_scale(void *data, struct wl_output *output, int32_t factor) {
+ struct swaylock_surface *surface = data;
+ surface->scale = factor;
+ if (surface->state->run_display) {
+ render_frames(surface->state);
}
- if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
- return false;
- }
- return true;
}
-void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
- int redraw_screen = 0;
- char *password_realloc;
- int i;
-
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- switch (sym) {
- case XKB_KEY_KP_Enter:
- case XKB_KEY_Return:
- render_data.auth_state = AUTH_STATE_VALIDATING;
-
- render(&render_data, config);
- // Make sure our render call will actually be displayed on the screen
- wl_dispatch_events();
-
- if (verify_password()) {
- exit(0);
- }
-
- render_data.auth_state = AUTH_STATE_INVALID;
- redraw_screen = 1;
-
- password_size = 1024;
- password = malloc(password_size);
- password[0] = '\0';
- break;
- case XKB_KEY_BackSpace:
- i = strlen(password);
- if (i > 0) {
- password[i - 1] = '\0';
- render_data.auth_state = AUTH_STATE_BACKSPACE;
- redraw_screen = 1;
- }
- break;
- case XKB_KEY_Control_L:
- case XKB_KEY_Control_R:
- case XKB_KEY_Shift_L:
- case XKB_KEY_Shift_R:
- case XKB_KEY_Caps_Lock:
- case XKB_KEY_Shift_Lock:
- case XKB_KEY_Meta_L:
- case XKB_KEY_Meta_R:
- case XKB_KEY_Alt_L:
- case XKB_KEY_Alt_R:
- case XKB_KEY_Super_L:
- case XKB_KEY_Super_R:
- case XKB_KEY_Hyper_L:
- case XKB_KEY_Hyper_R:
- break; // don't draw screen on modifier keys
- case XKB_KEY_Escape:
- case XKB_KEY_u:
- case XKB_KEY_U:
- // clear password buffer on ctrl-u (or escape for i3lock compatibility)
- if (sym == XKB_KEY_Escape || xkb_state_mod_name_is_active(registry->input->xkb.state,
- XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) {
- render_data.auth_state = AUTH_STATE_BACKSPACE;
- redraw_screen = 1;
-
- password_size = 1024;
- free(password);
- password = malloc(password_size);
- password[0] = '\0';
- break;
- }
- /* fallthrough */
- default:
- render_data.auth_state = AUTH_STATE_INPUT;
- redraw_screen = 1;
- i = strlen(password);
- if (i + 1 == password_size) {
- password_size += 1024;
- password_realloc = realloc(password, password_size);
- // reset password if realloc fails.
- if (password_realloc == NULL) {
- password_size = 1024;
- free(password);
- password = malloc(password_size);
- password[0] = '\0';
- break;
- } else {
- password = password_realloc;
- }
- }
- password[i] = (char)codepoint;
- password[i + 1] = '\0';
- break;
- }
- if (redraw_screen) {
- render(&render_data, config);
- wl_dispatch_events();
- // Hide the indicator after a couple of seconds
- alarm(5);
- }
+struct wl_output_listener output_listener = {
+ .geometry = output_geometry,
+ .mode = output_mode,
+ .done = output_done,
+ .scale = output_scale,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ struct swaylock_state *state = data;
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ state->compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 3);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ state->shm = wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ struct wl_seat *seat = wl_registry_bind(
+ registry, name, &wl_seat_interface, 1);
+ wl_seat_add_listener(seat, &seat_listener, state);
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ state->layer_shell = wl_registry_bind(
+ registry, name, &zwlr_layer_shell_v1_interface, 1);
+ } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
+ state->input_inhibit_manager = wl_registry_bind(
+ registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ struct swaylock_surface *surface =
+ calloc(1, sizeof(struct swaylock_surface));
+ surface->state = state;
+ surface->output = wl_registry_bind(registry, name,
+ &wl_output_interface, 3);
+ wl_output_add_listener(surface->output, &output_listener, surface);
+ wl_list_insert(&state->surfaces, &surface->link);
}
}
-void render_color(struct window *window, uint32_t color) {
- cairo_set_source_u32(window->cairo, color);
- cairo_paint(window->cairo);
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
}
-void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) {
- double width = cairo_image_surface_get_width(image);
- double height = cairo_image_surface_get_height(image);
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- switch (scaling_mode) {
- case SCALING_MODE_STRETCH:
- cairo_scale(window->cairo,
- (double) wwidth / width,
- (double) wheight / height);
- cairo_set_source_surface(window->cairo, image, 0, 0);
- break;
- case SCALING_MODE_FILL:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
- if (window_ratio > bg_ratio) {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- } else {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- }
- break;
- }
- case SCALING_MODE_FIT:
- {
- double window_ratio = (double) wwidth / wheight;
- double bg_ratio = width / height;
-
- if (window_ratio > bg_ratio) {
- double scale = (double) wheight / height;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 / scale - width/2,
- 0);
- } else {
- double scale = (double) wwidth / width;
- cairo_scale(window->cairo, scale, scale);
- cairo_set_source_surface(window->cairo, image,
- 0,
- (double) wheight/2 / scale - height/2);
- }
- break;
- }
- case SCALING_MODE_CENTER:
- cairo_set_source_surface(window->cairo, image,
- (double) wwidth/2 - width/2,
- (double) wheight/2 - height/2);
- break;
- case SCALING_MODE_TILE:
- {
- cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
- cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
- cairo_set_source(window->cairo, pattern);
- break;
- }
- }
-
- cairo_paint(window->cairo);
-}
-
-cairo_surface_t *load_image(char *image_path) {
- cairo_surface_t *image = NULL;
-
-#ifdef WITH_GDK_PIXBUF
- GError *err = NULL;
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
- if (!pixbuf) {
- sway_abort("Failed to load background image: %s", err->message);
- }
- image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
- g_object_unref(pixbuf);
-#else
- image = cairo_image_surface_create_from_png(image_path);
-#endif //WITH_GDK_PIXBUF
- if (!image) {
- sway_abort("Failed to read background image.");
- }
-
- return image;
-}
+static struct swaylock_state state;
int main(int argc, char **argv) {
- const char *scaling_mode_str = "fit", *socket_path = NULL;
- int i;
- void *images = NULL;
- config = init_config();
-
- render_data.num_images = 0;
- render_data.color_set = 0;
- render_data.color = 0xFFFFFFFF;
- render_data.auth_state = AUTH_STATE_IDLE;
-
- init_log(L_INFO);
- // Install SIGALARM handler (for hiding the typing indicator)
- signal(SIGALRM, sigalarm_handler);
-
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"color", required_argument, NULL, 'c'},
{"image", required_argument, NULL, 'i'},
- {"scaling", required_argument, NULL, 0},
+ {"scaling", required_argument, NULL, 's'},
{"tiling", no_argument, NULL, 't'},
{"version", no_argument, NULL, 'v'},
{"socket", required_argument, NULL, 'p'},
{"no-unlock-indicator", no_argument, NULL, 'u'},
{"daemonize", no_argument, NULL, 'f'},
- {"font", required_argument, NULL, 0},
- {"line-uses-ring", no_argument, NULL, 'r'},
- {"line-uses-inside", no_argument, NULL, 's'},
- {"textcolor", required_argument, NULL, 0},
- {"insidevercolor", required_argument, NULL, 0},
- {"insidewrongcolor", required_argument, NULL, 0},
- {"insidecolor", required_argument, NULL, 0},
- {"ringvercolor", required_argument, NULL, 0},
- {"ringwrongcolor", required_argument, NULL, 0},
- {"ringcolor", required_argument, NULL, 0},
- {"linecolor", required_argument, NULL, 0},
- {"separatorcolor", required_argument, NULL, 0},
- {"keyhlcolor", required_argument, NULL, 0},
- {"bshlcolor", required_argument, NULL, 0},
- {"indicator-radius", required_argument, NULL, 0},
- {"indicator-thickness", required_argument, NULL, 0},
{0, 0, 0, 0}
};
@@ -390,415 +147,124 @@ int main(int argc, char **argv) {
"\n"
" -h, --help Show help message and quit.\n"
" -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\n"
- " --scaling Scaling mode: stretch, fill, fit, center, tile.\n"
+ " -s, --scaling Scaling mode: stretch, fill, fit, center, tile.\n"
" -t, --tiling Same as --scaling=tile.\n"
" -v, --version Show the version number and quit.\n"
" -i, --image [<output>:]<path> Display the given image.\n"
" -u, --no-unlock-indicator Disable the unlock indicator.\n"
- " -f, --daemonize Detach from the controlling terminal.\n"
- " --socket <socket> Use the specified socket.\n"
- " For more information see `man swaylock`\n";
+ " -f, --daemonize Detach from the controlling terminal.\n"
+ " --socket <socket> Use the specified socket.\n";
-
- registry = registry_poll();
+ struct swaylock_args args = {
+ .mode = BACKGROUND_MODE_SOLID_COLOR,
+ .color = 0xFFFFFFFF,
+ .show_indicator = true,
+ };
+ cairo_surface_t *background_image = NULL;
+ state.args = args;
+ wlr_log_init(L_DEBUG, NULL);
int c;
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hc:i:srtvuf", long_options, &option_index);
+ c = getopt_long(argc, argv, "hc:i:s:tvuf", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
- case 'c':
- {
- render_data.color = parse_color(optarg);
- render_data.color_set = 1;
+ case 'c': {
+ state.args.color = parse_color(optarg);
+ state.args.mode = BACKGROUND_MODE_SOLID_COLOR;
break;
}
case 'i':
- {
- char *image_path = strchr(optarg, ':');
- if (image_path == NULL) {
- if (render_data.num_images == 0) {
- // Provided image without output
- render_data.image = load_image(optarg);
- render_data.num_images = -1;
- } else {
- sway_log(L_ERROR, "output must be defined for all --images or no --images");
- exit(EXIT_FAILURE);
- }
- } else {
- // Provided image for all outputs
- if (render_data.num_images == 0) {
- images = calloc(registry->outputs->length, sizeof(char*) * 2);
- } else if (render_data.num_images == -1) {
- sway_log(L_ERROR, "output must be defined for all --images or no --images");
- exit(EXIT_FAILURE);
- }
-
- image_path[0] = '\0';
- ((char**) images)[render_data.num_images * 2] = optarg;
- ((char**) images)[render_data.num_images++ * 2 + 1] = ++image_path;
+ // TODO: Multiple background images (bleh)
+ background_image = load_background_image(optarg);
+ if (!background_image) {
+ return 1;
}
+ state.args.mode = BACKGROUND_MODE_FILL;
break;
- }
- case 't':
- scaling_mode_str = "tile";
+ case 's':
+ state.args.mode = parse_background_mode(optarg);
+ if (state.args.mode == BACKGROUND_MODE_INVALID) {
+ return 1;
+ }
break;
- case 'p':
- socket_path = optarg;
+ case 't':
+ state.args.mode = BACKGROUND_MODE_TILE;
break;
case 'v':
- fprintf(stdout, "swaylock version " SWAY_VERSION "\n");
- exit(EXIT_SUCCESS);
- break;
+#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
+ fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n",
+ SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH);
+#else
+ fprintf(stdout, "version unknown\n");
+#endif
+ return 0;
case 'u':
- show_indicator = false;
- break;
- case 'f': {
- pid_t t = fork();
- if (t == -1) {
- sway_log(L_ERROR, "daemon call failed");
- exit(EXIT_FAILURE);
- } else if (t > 0) {
- exit(0);
- }
- break;
- }
- case 'r':
- if (line_source != LINE_SOURCE_DEFAULT) {
- sway_log(L_ERROR, "line source options conflict");
- exit(EXIT_FAILURE);
- }
- line_source = LINE_SOURCE_RING;
+ state.args.show_indicator = false;
break;
- case 's':
- if (line_source != LINE_SOURCE_DEFAULT) {
- sway_log(L_ERROR, "line source options conflict");
- exit(EXIT_FAILURE);
- }
- line_source = LINE_SOURCE_INSIDE;
- break;
- case 0:
- if (strcmp(long_options[option_index].name, "font") == 0) {
- free(config->font);
- config->font = strdup(optarg);
- } else if (strcmp(long_options[option_index].name, "scaling") == 0) {
- scaling_mode_str = optarg;
- } else if (strcmp(long_options[option_index].name, "textcolor") == 0) {
- config->colors.text = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) {
- config->colors.validating.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) {
- config->colors.invalid.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "insidecolor") == 0) {
- config->colors.normal.inner_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) {
- config->colors.validating.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringwrongcolor") == 0) {
- config->colors.invalid.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "ringcolor") == 0) {
- config->colors.normal.outer_ring = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "linecolor") == 0) {
- config->colors.line = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "separatorcolor") == 0) {
- config->colors.separator = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "keyhlcolor") == 0) {
- config->colors.input_cursor = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "bshlcolor") == 0) {
- config->colors.backspace_cursor = parse_color(optarg);
- } else if (strcmp(long_options[option_index].name, "indicator-radius") == 0) {
- config->radius = atoi(optarg);
- } else if (strcmp(long_options[option_index].name, "indicator-thickness") == 0) {
- config->thickness = atoi(optarg);
- }
+ case 'f':
+ daemonize();
break;
default:
fprintf(stderr, "%s", usage);
- exit(EXIT_FAILURE);
+ return 1;
}
}
- render_data.scaling_mode = SCALING_MODE_STRETCH;
- if (strcmp(scaling_mode_str, "stretch") == 0) {
- render_data.scaling_mode = SCALING_MODE_STRETCH;
- } else if (strcmp(scaling_mode_str, "fill") == 0) {
- render_data.scaling_mode = SCALING_MODE_FILL;
- } else if (strcmp(scaling_mode_str, "fit") == 0) {
- render_data.scaling_mode = SCALING_MODE_FIT;
- } else if (strcmp(scaling_mode_str, "center") == 0) {
- render_data.scaling_mode = SCALING_MODE_CENTER;
- } else if (strcmp(scaling_mode_str, "tile") == 0) {
- render_data.scaling_mode = SCALING_MODE_TILE;
- } else {
- sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
+ wl_list_init(&state.surfaces);
+ state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ state.display = wl_display_connect(NULL);
+ assert(state.display);
+
+ struct wl_registry *registry = wl_display_get_registry(state.display);
+ wl_registry_add_listener(registry, &registry_listener, &state);
+ wl_display_roundtrip(state.display);
+ assert(state.compositor && state.layer_shell && state.shm);
+ if (!state.input_inhibit_manager) {
+ wlr_log(L_ERROR, "Compositor does not support the input inhibitor "
+ "protocol, refusing to run insecurely");
}
- password_size = 1024;
- password = malloc(password_size);
- password[0] = '\0';
- render_data.surfaces = create_list();
- if (!socket_path) {
- socket_path = get_socketpath();
- if (!socket_path) {
- sway_abort("Unable to retrieve socket path");
- }
- }
-
- if (!registry) {
- sway_abort("Unable to connect to wayland compositor");
- }
-
- if (!registry->swaylock) {
- sway_abort("swaylock requires the compositor to support the swaylock extension.");
- }
-
- if (registry->pointer) {
- // We don't want swaylock to have a pointer
- wl_pointer_destroy(registry->pointer);
- registry->pointer = NULL;
- }
-
- for (i = 0; i < registry->outputs->length; ++i) {
- struct output_state *output = registry->outputs->items[i];
- struct window *window = window_setup(registry,
- output->width, output->height, output->scale, true);
- if (!window) {
- sway_abort("Failed to create surfaces.");
- }
- list_add(render_data.surfaces, window);
- }
-
- registry->input->notify = notify_key;
-
- // Different background for the output
- if (render_data.num_images >= 1) {
- char **displays_paths = images;
- render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*));
-
- int socketfd = ipc_open_socket(socket_path);
- uint32_t len = 0;
- char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len);
- struct json_object *json_outputs = json_tokener_parse(outputs);
-
- for (i = 0; i < registry->outputs->length; ++i) {
- if (displays_paths[i * 2] != NULL) {
- for (int j = 0;; ++j) {
- if (j >= json_object_array_length(json_outputs)) {
- sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]);
- exit(EXIT_FAILURE);
- }
-
- struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j);
- if (!json_object_object_get_ex(at_j, "name", &dsp_name)) {
- sway_abort("output doesn't have a name field");
- }
- if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) {
- render_data.images[j] = load_image(displays_paths[i * 2 + 1]);
- break;
- }
- }
- }
- }
-
- json_object_put(json_outputs);
- close(socketfd);
- free(displays_paths);
+ if (wl_list_empty(&state.surfaces)) {
+ wlr_log(L_DEBUG, "Exiting - no outputs to show on.");
+ return 0;
}
- render(&render_data, config);
- bool locked = false;
- while (wl_display_dispatch(registry->display) != -1) {
- if (!locked) {
- for (i = 0; i < registry->outputs->length; ++i) {
- struct output_state *output = registry->outputs->items[i];
- struct window *window = render_data.surfaces->items[i];
- lock_set_lock_surface(registry->swaylock, output->output, window->surface);
- }
- locked = true;
- }
+ struct swaylock_surface *surface;
+ wl_list_for_each(surface, &state.surfaces, link) {
+ surface->image = background_image;
+
+ surface->surface = wl_compositor_create_surface(state.compositor);
+ assert(surface->surface);
+
+ surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
+ state.layer_shell, surface->surface, surface->output,
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen");
+ assert(surface->layer_surface);
+
+ zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0);
+ zwlr_layer_surface_v1_set_anchor(surface->layer_surface,
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
+ zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1);
+ zwlr_layer_surface_v1_set_keyboard_interactivity(
+ surface->layer_surface, true);
+ zwlr_layer_surface_v1_add_listener(surface->layer_surface,
+ &layer_surface_listener, surface);
+ wl_surface_commit(surface->surface);
+ wl_display_roundtrip(state.display);
}
- // Free surfaces
- if (render_data.num_images == -1) {
- cairo_surface_destroy(render_data.image);
- } else if (render_data.num_images >= 1) {
- for (i = 0; i < registry->outputs->length; ++i) {
- if (render_data.images[i] != NULL) {
- cairo_surface_destroy(render_data.images[i]);
- }
- }
- free(render_data.images);
- }
+ zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
- for (i = 0; i < render_data.surfaces->length; ++i) {
- struct window *window = render_data.surfaces->items[i];
- window_teardown(window);
+ state.run_display = true;
+ while (wl_display_dispatch(state.display) != -1 && state.run_display) {
+ // This space intentionally left blank
}
- list_free(render_data.surfaces);
- registry_teardown(registry);
-
- free_config(config);
-
return 0;
}
-
-void render(struct render_data *render_data, struct lock_config *config) {
- int i;
- for (i = 0; i < render_data->surfaces->length; ++i) {
- sway_log(L_DEBUG, "Render surface %d of %d", i, render_data->surfaces->length);
- struct window *window = render_data->surfaces->items[i];
- if (!window_prerender(window) || !window->cairo) {
- continue;
- }
- int wwidth = window->width * window->scale;
- int wheight = window->height * window->scale;
-
- cairo_save(window->cairo);
- cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR);
- cairo_paint(window->cairo);
- cairo_restore(window->cairo);
-
- // Reset the transformation matrix
- cairo_identity_matrix(window->cairo);
-
- if (render_data->num_images == 0 || render_data->color_set) {
- render_color(window, render_data->color);
- }
-
- if (render_data->num_images == -1) {
- // One background for all
- render_image(window, render_data->image, render_data->scaling_mode);
- } else if (render_data->num_images >= 1) {
- // Different backgrounds
- if (render_data->images[i] != NULL) {
- render_image(window, render_data->images[i], render_data->scaling_mode);
- }
- }
-
- // Reset the transformation matrix again
- cairo_identity_matrix(window->cairo);
-
- // Draw specific values (copied from i3)
- const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
- const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
-
- // Add visual indicator
- if (show_indicator && render_data->auth_state != AUTH_STATE_IDLE) {
- // Draw circle
- cairo_set_line_width(window->cairo, config->thickness);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, 0, 2 * M_PI);
- switch (render_data->auth_state) {
- case AUTH_STATE_INPUT:
- case AUTH_STATE_BACKSPACE: {
- cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- case AUTH_STATE_VALIDATING: {
- cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- case AUTH_STATE_INVALID: {
- cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
- cairo_fill_preserve(window->cairo);
- cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
- cairo_stroke(window->cairo);
- } break;
- default: break;
- }
-
- // Draw a message
- char *text = NULL;
- cairo_set_source_u32(window->cairo, config->colors.text);
- cairo_select_font_face(window->cairo, config->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
- cairo_set_font_size(window->cairo, config->radius/3.0f);
- switch (render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- text = "verifying";
- break;
- case AUTH_STATE_INVALID:
- text = "wrong";
- break;
- default: break;
- }
-
- if (text) {
- cairo_text_extents_t extents;
- double x, y;
-
- cairo_text_extents(window->cairo, text, &extents);
- x = wwidth/2 - ((extents.width/2) + extents.x_bearing);
- y = wheight/2 - ((extents.height/2) + extents.y_bearing);
-
- cairo_move_to(window->cairo, x, y);
- cairo_show_text(window->cairo, text);
- cairo_close_path(window->cairo);
- cairo_new_sub_path(window->cairo);
- }
-
- // Typing indicator: Highlight random part on keypress
- if (render_data->auth_state == AUTH_STATE_INPUT || render_data->auth_state == AUTH_STATE_BACKSPACE) {
- static double highlight_start = 0;
- highlight_start += (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_RANGE);
- if (render_data->auth_state == AUTH_STATE_INPUT) {
- cairo_set_source_u32(window->cairo, config->colors.input_cursor);
- } else {
- cairo_set_source_u32(window->cairo, config->colors.backspace_cursor);
- }
- cairo_stroke(window->cairo);
-
- // Draw borders
- cairo_set_source_u32(window->cairo, config->colors.separator);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start, highlight_start + TYPE_INDICATOR_BORDER_THICKNESS);
- cairo_stroke(window->cairo);
-
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius, highlight_start + TYPE_INDICATOR_RANGE, (highlight_start + TYPE_INDICATOR_RANGE) + TYPE_INDICATOR_BORDER_THICKNESS);
- cairo_stroke(window->cairo);
- }
-
- switch(line_source) {
- case LINE_SOURCE_RING:
- switch(render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- cairo_set_source_u32(window->cairo, config->colors.validating.outer_ring);
- break;
- case AUTH_STATE_INVALID:
- cairo_set_source_u32(window->cairo, config->colors.invalid.outer_ring);
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.normal.outer_ring);
- }
- break;
- case LINE_SOURCE_INSIDE:
- switch(render_data->auth_state) {
- case AUTH_STATE_VALIDATING:
- cairo_set_source_u32(window->cairo, config->colors.validating.inner_ring);
- break;
- case AUTH_STATE_INVALID:
- cairo_set_source_u32(window->cairo, config->colors.invalid.inner_ring);
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.normal.inner_ring);
- break;
- }
- break;
- default:
- cairo_set_source_u32(window->cairo, config->colors.line);
- break;
- }
- // Draw inner + outer border of the circle
- cairo_set_line_width(window->cairo, 2.0);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius - config->thickness/2, 0, 2*M_PI);
- cairo_stroke(window->cairo);
- cairo_arc(window->cairo, wwidth/2, wheight/2, config->radius + config->thickness/2, 0, 2*M_PI);
- cairo_stroke(window->cairo);
- }
- window_render(window);
- }
-}
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 @@
+executable(
+ 'swaylock', [
+ 'main.c',
+ 'password.c',
+ 'render.c',
+ 'seat.c'
+ ],
+ include_directories: [sway_inc],
+ dependencies: [
+ cairo,
+ client_protos,
+ gdk_pixbuf,
+ libpam,
+ math,
+ pango,
+ pangocairo,
+ xkbcommon,
+ wayland_client,
+ wlroots,
+ ],
+ link_with: [lib_sway_common, lib_sway_client],
+ install: true
+)
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 @@
+#include <assert.h>
+#include <pwd.h>
+#include <security/pam_appl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "swaylock/swaylock.h"
+#include "swaylock/seat.h"
+#include "unicode.h"
+
+static int function_conversation(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *data) {
+ struct swaylock_password *pw = data;
+ /* PAM expects an array of responses, one for each message */
+ struct pam_response *pam_reply = calloc(
+ num_msg, sizeof(struct pam_response));
+ *resp = pam_reply;
+ for (int i = 0; i < num_msg; ++i) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ case PAM_PROMPT_ECHO_ON:
+ pam_reply[i].resp = pw->buffer;
+ break;
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+static bool attempt_password(struct swaylock_password *pw) {
+ struct passwd *passwd = getpwuid(getuid());
+ char *username = passwd->pw_name;
+ const struct pam_conv local_conversation = {
+ function_conversation, pw
+ };
+ pam_handle_t *local_auth_handle = NULL;
+ int pam_err;
+ if ((pam_err = pam_start("swaylock", username,
+ &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "PAM returned error %d", pam_err);
+ }
+ if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "pam_authenticate failed");
+ goto fail;
+ }
+ if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
+ wlr_log(L_ERROR, "pam_end failed");
+ goto fail;
+ }
+ // PAM frees this
+ pw->buffer = NULL;
+ pw->len = pw->size = 0;
+ return true;
+fail:
+ // PAM frees this
+ pw->buffer = NULL;
+ pw->len = pw->size = 0;
+ return false;
+}
+
+static bool backspace(struct swaylock_password *pw) {
+ if (pw->len != 0) {
+ pw->buffer[--pw->len] = 0;
+ return true;
+ }
+ return false;
+}
+
+static void append_ch(struct swaylock_password *pw, uint32_t codepoint) {
+ if (!pw->buffer) {
+ pw->size = 8;
+ if (!(pw->buffer = malloc(pw->size))) {
+ // TODO: Display error
+ return;
+ }
+ pw->buffer[0] = 0;
+ }
+ size_t utf8_size = utf8_chsize(codepoint);
+ if (pw->len + utf8_size + 1 >= pw->size) {
+ size_t size = pw->size * 2;
+ char *buffer = realloc(pw->buffer, size);
+ if (!buffer) {
+ // TODO: Display error
+ return;
+ }
+ pw->size = size;
+ pw->buffer = buffer;
+ }
+ utf8_encode(&pw->buffer[pw->len], codepoint);
+ pw->buffer[pw->len + utf8_size] = 0;
+ pw->len += utf8_size;
+}
+
+void swaylock_handle_key(struct swaylock_state *state,
+ xkb_keysym_t keysym, uint32_t codepoint) {
+ switch (keysym) {
+ case XKB_KEY_KP_Enter: /* fallthrough */
+ case XKB_KEY_Return:
+ state->auth_state = AUTH_STATE_VALIDATING;
+ render_frames(state);
+ wl_display_roundtrip(state->display);
+ if (attempt_password(&state->password)) {
+ state->run_display = false;
+ break;
+ }
+ state->auth_state = AUTH_STATE_INVALID;
+ render_frames(state);
+ break;
+ case XKB_KEY_BackSpace:
+ if (backspace(&state->password)) {
+ state->auth_state = AUTH_STATE_BACKSPACE;
+ render_frames(state);
+ }
+ break;
+ default:
+ if (codepoint) {
+ append_ch(&state->password, codepoint);
+ state->auth_state = AUTH_STATE_INPUT;
+ render_frames(state);
+ }
+ break;
+ }
+}
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 @@
+#define _POSIX_C_SOURCE 199506L
+#include <math.h>
+#include <stdlib.h>
+#include <wayland-client.h>
+#include "cairo.h"
+#include "background-image.h"
+#include "swaylock/swaylock.h"
+
+#define M_PI 3.14159265358979323846
+const int ARC_RADIUS = 50;
+const int ARC_THICKNESS = 10;
+const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;
+const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f;
+
+void render_frame(struct swaylock_surface *surface) {
+ struct swaylock_state *state = surface->state;
+
+ int buffer_width = surface->width * surface->scale;
+ int buffer_height = surface->height * surface->scale;
+
+ surface->current_buffer = get_next_buffer(state->shm,
+ surface->buffers, buffer_width, buffer_height);
+ cairo_t *cairo = surface->current_buffer->cairo;
+ cairo_identity_matrix(cairo);
+
+ if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) {
+ cairo_set_source_u32(cairo, state->args.color);
+ cairo_paint(cairo);
+ } else {
+ render_background_image(cairo, surface->image,
+ state->args.mode, buffer_width, buffer_height);
+ }
+ cairo_identity_matrix(cairo);
+
+ int arc_radius = ARC_RADIUS * surface->scale;
+ int arc_thickness = ARC_THICKNESS * surface->scale;
+ float type_indicator_border_thickness =
+ TYPE_INDICATOR_BORDER_THICKNESS * surface->scale;
+
+ if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) {
+ // Draw circle
+ cairo_set_line_width(cairo, arc_thickness);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2, arc_radius, 0, 2 * M_PI);
+ switch (state->auth_state) {
+ case AUTH_STATE_INPUT:
+ case AUTH_STATE_BACKSPACE: {
+ cairo_set_source_rgba(cairo, 0, 0, 0, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0);
+ cairo_stroke(cairo);
+ } break;
+ case AUTH_STATE_VALIDATING: {
+ cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255);
+ cairo_stroke(cairo);
+ } break;
+ case AUTH_STATE_INVALID: {
+ cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75);
+ cairo_fill_preserve(cairo);
+ cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0);
+ cairo_stroke(cairo);
+ } break;
+ default: break;
+ }
+
+ // Draw a message
+ char *text = NULL;
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_select_font_face(cairo, "sans-serif",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cairo, arc_radius / 3.0f);
+ switch (state->auth_state) {
+ case AUTH_STATE_VALIDATING:
+ text = "verifying";
+ break;
+ case AUTH_STATE_INVALID:
+ text = "wrong";
+ break;
+ default: break;
+ }
+
+ if (text) {
+ cairo_text_extents_t extents;
+ double x, y;
+ cairo_text_extents(cairo, text, &extents);
+ x = (buffer_width / 2) -
+ (extents.width / 2 + extents.x_bearing);
+ y = (buffer_height / 2) -
+ (extents.height / 2 + extents.y_bearing);
+
+ cairo_move_to(cairo, x, y);
+ cairo_show_text(cairo, text);
+ cairo_close_path(cairo);
+ cairo_new_sub_path(cairo);
+ }
+
+ // Typing indicator: Highlight random part on keypress
+ if (state->auth_state == AUTH_STATE_INPUT
+ || state->auth_state == AUTH_STATE_BACKSPACE) {
+ static double highlight_start = 0;
+ highlight_start +=
+ (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5;
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start,
+ highlight_start + TYPE_INDICATOR_RANGE);
+ if (state->auth_state == AUTH_STATE_INPUT) {
+ cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0);
+ } else {
+ cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0);
+ }
+ cairo_stroke(cairo);
+
+ // Draw borders
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start,
+ highlight_start + type_indicator_border_thickness);
+ cairo_stroke(cairo);
+
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius, highlight_start + TYPE_INDICATOR_RANGE,
+ highlight_start + TYPE_INDICATOR_RANGE +
+ type_indicator_border_thickness);
+ cairo_stroke(cairo);
+ }
+
+ // Draw inner + outer border of the circle
+ cairo_set_source_rgb(cairo, 0, 0, 0);
+ cairo_set_line_width(cairo, 2.0 * surface->scale);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius - arc_thickness / 2, 0, 2 * M_PI);
+ cairo_stroke(cairo);
+ cairo_arc(cairo, buffer_width / 2, buffer_height / 2,
+ arc_radius + arc_thickness / 2, 0, 2 * M_PI);
+ cairo_stroke(cairo);
+ }
+
+ wl_surface_set_buffer_scale(surface->surface, surface->scale);
+ wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0);
+ wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height);
+ wl_surface_commit(surface->surface);
+}
+
+void render_frames(struct swaylock_state *state) {
+ struct swaylock_surface *surface;
+ wl_list_for_each(surface, &state->surfaces, link) {
+ render_frame(surface);
+ }
+}
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 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "swaylock/swaylock.h"
+#include "swaylock/seat.h"
+
+const char *XKB_MASK_NAMES[MASK_LAST] = {
+ XKB_MOD_NAME_SHIFT,
+ XKB_MOD_NAME_CAPS,
+ XKB_MOD_NAME_CTRL,
+ XKB_MOD_NAME_ALT,
+ "Mod2",
+ "Mod3",
+ XKB_MOD_NAME_LOGO,
+ "Mod5",
+};
+
+const enum mod_bit XKB_MODS[MASK_LAST] = {
+ MOD_SHIFT,
+ MOD_CAPS,
+ MOD_CTRL,
+ MOD_ALT,
+ MOD_MOD2,
+ MOD_MOD3,
+ MOD_LOGO,
+ MOD_MOD5
+};
+
+static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t format, int32_t fd, uint32_t size) {
+ struct swaylock_state *state = data;
+ if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
+ close(fd);
+ wlr_log(L_ERROR, "Unknown keymap format %d, aborting", format);
+ exit(1);
+ }
+ char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ if (map_shm == MAP_FAILED) {
+ close(fd);
+ wlr_log(L_ERROR, "Unable to initialize keymap shm, aborting");
+ exit(1);
+ }
+ struct xkb_keymap *keymap = xkb_keymap_new_from_string(
+ state->xkb.context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
+ munmap(map_shm, size);
+ close(fd);
+ assert(keymap);
+ struct xkb_state *xkb_state = xkb_state_new(keymap);
+ assert(xkb_state);
+ xkb_keymap_unref(state->xkb.keymap);
+ xkb_state_unref(state->xkb.state);
+ state->xkb.keymap = keymap;
+ state->xkb.state = xkb_state;
+}
+
+static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
+ // Who cares
+}
+
+static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, struct wl_surface *surface) {
+ // Who cares
+}
+
+static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
+ struct swaylock_state *state = data;
+ enum wl_keyboard_key_state key_state = _key_state;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8);
+ uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ?
+ key + 8 : 0;
+ uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode);
+ if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ swaylock_handle_key(state, sym, codepoint);
+ }
+}
+
+static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
+ uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {
+ struct swaylock_state *state = data;
+ xkb_state_update_mask(state->xkb.state,
+ mods_depressed, mods_latched, mods_locked, 0, 0, group);
+ xkb_mod_mask_t mask = xkb_state_serialize_mods(state->xkb.state,
+ XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
+ state->xkb.modifiers = 0;
+ for (uint32_t i = 0; i < MASK_LAST; ++i) {
+ if (mask & state->xkb.masks[i]) {
+ state->xkb.modifiers |= XKB_MODS[i];
+ }
+ }
+}
+
+static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
+ int32_t rate, int32_t delay) {
+ // TODO
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ .keymap = keyboard_keymap,
+ .enter = keyboard_enter,
+ .leave = keyboard_leave,
+ .key = keyboard_key,
+ .modifiers = keyboard_modifiers,
+ .repeat_info = keyboard_repeat_info,
+};
+
+static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0);
+}
+
+static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface) {
+ // Who cares
+}
+
+static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // Who cares
+}
+
+static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
+ // Who cares
+}
+
+static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value) {
+ // Who cares
+}
+
+static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
+ // Who cares
+}
+
+static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis_source) {
+ // Who cares
+}
+
+static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis) {
+ // Who cares
+}
+
+static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
+ uint32_t axis, int32_t discrete) {
+ // Who cares
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ .enter = wl_pointer_enter,
+ .leave = wl_pointer_leave,
+ .motion = wl_pointer_motion,
+ .button = wl_pointer_button,
+ .axis = wl_pointer_axis,
+ .frame = wl_pointer_frame,
+ .axis_source = wl_pointer_axis_source,
+ .axis_stop = wl_pointer_axis_stop,
+ .axis_discrete = wl_pointer_axis_discrete,
+};
+
+static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+ enum wl_seat_capability caps) {
+ struct swaylock_state *state = data;
+ if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
+ struct wl_pointer *pointer = wl_seat_get_pointer(wl_seat);
+ wl_pointer_add_listener(pointer, &pointer_listener, NULL);
+ }
+ if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
+ struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat);
+ wl_keyboard_add_listener(keyboard, &keyboard_listener, state);
+ }
+}
+
+static void seat_handle_name(void *data, struct wl_seat *wl_seat,
+ const char *name) {
+ // Who cares
+}
+
+const struct wl_seat_listener seat_listener = {
+ .capabilities = seat_handle_capabilities,
+ .name = seat_handle_name,
+};