mirror of
https://github.com/swaywm/sway.git
synced 2024-12-29 00:16:22 +01:00
Merge pull request #1705 from swaywm/swaylock-layers
Port swaylock to layer shell
This commit is contained in:
commit
f2153f3f28
21 changed files with 1253 additions and 917 deletions
119
common/background-image.c
Normal file
119
common/background-image.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
101
common/unicode.c
Normal file
101
common/unicode.c
Normal file
|
@ -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;
|
||||
}
|
20
include/background-image.h
Normal file
20
include/background-image.h
Normal file
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
38
include/swaylock/seat.h
Normal file
38
include/swaylock/seat.h
Normal file
|
@ -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
|
|
@ -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 background_mode mode;
|
||||
bool show_indicator;
|
||||
};
|
||||
|
||||
struct swaylock_password {
|
||||
size_t size;
|
||||
size_t len;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct lock_colors {
|
||||
uint32_t inner_ring;
|
||||
uint32_t outer_ring;
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
int radius;
|
||||
int thickness;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
33
include/unicode.h
Normal file
33
include/unicode.h
Normal file
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
|
|
@ -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 = []
|
||||
|
|
67
protocols/wlr-input-inhibitor-unstable-v1.xml
Normal file
67
protocols/wlr-input-inhibitor-unstable-v1.xml
Normal file
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
@ -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);
|
||||
|
|
139
swaybg/main.c
139
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
|
||||
}
|
||||
|
|
906
swaylock/main.c
906
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"
|
||||
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.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);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void render_color(struct window *window, uint32_t color) {
|
||||
cairo_set_source_u32(window->cairo, color);
|
||||
cairo_paint(window->cairo);
|
||||
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_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;
|
||||
|
||||
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);
|
||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name) {
|
||||
// who cares
|
||||
}
|
||||
|
||||
cairo_surface_t *load_image(char *image_path) {
|
||||
cairo_surface_t *image = NULL;
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = handle_global,
|
||||
.global_remove = handle_global_remove,
|
||||
};
|
||||
|
||||
#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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
scaling_mode_str = "tile";
|
||||
break;
|
||||
case 'p':
|
||||
socket_path = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
fprintf(stdout, "swaylock version " SWAY_VERSION "\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
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.mode = BACKGROUND_MODE_FILL;
|
||||
break;
|
||||
case 's':
|
||||
if (line_source != LINE_SOURCE_DEFAULT) {
|
||||
sway_log(L_ERROR, "line source options conflict");
|
||||
exit(EXIT_FAILURE);
|
||||
state.args.mode = parse_background_mode(optarg);
|
||||
if (state.args.mode == BACKGROUND_MODE_INVALID) {
|
||||
return 1;
|
||||
}
|
||||
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 't':
|
||||
state.args.mode = BACKGROUND_MODE_TILE;
|
||||
break;
|
||||
case 'v':
|
||||
#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':
|
||||
state.args.show_indicator = false;
|
||||
break;
|
||||
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, ®istry_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 (wl_list_empty(&state.surfaces)) {
|
||||
wlr_log(L_DEBUG, "Exiting - no outputs to show on.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!registry) {
|
||||
sway_abort("Unable to connect to wayland compositor");
|
||||
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);
|
||||
}
|
||||
|
||||
if (!registry->swaylock) {
|
||||
sway_abort("swaylock requires the compositor to support the swaylock extension.");
|
||||
zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
|
||||
|
||||
state.run_display = true;
|
||||
while (wl_display_dispatch(state.display) != -1 && state.run_display) {
|
||||
// This space intentionally left blank
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
23
swaylock/meson.build
Normal file
23
swaylock/meson.build
Normal file
|
@ -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
|
||||
)
|
126
swaylock/password.c
Normal file
126
swaylock/password.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
150
swaylock/render.c
Normal file
150
swaylock/render.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
190
swaylock/seat.c
Normal file
190
swaylock/seat.c
Normal file
|
@ -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,
|
||||
};
|
Loading…
Reference in a new issue