mirror of
https://github.com/swaywm/sway.git
synced 2024-11-23 08:21:28 +00:00
Merge pull request #1705 from swaywm/swaylock-layers
Port swaylock to layer shell
This commit is contained in:
commit
f2153f3f28
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(
|
lib_sway_common = static_library(
|
||||||
'sway-common',
|
'sway-common',
|
||||||
files(
|
files(
|
||||||
|
'background-image.c',
|
||||||
'cairo.c',
|
'cairo.c',
|
||||||
'ipc-client.c',
|
'ipc-client.c',
|
||||||
'log.c',
|
'log.c',
|
||||||
|
@ -19,8 +9,15 @@ lib_sway_common = static_library(
|
||||||
'pango.c',
|
'pango.c',
|
||||||
'readline.c',
|
'readline.c',
|
||||||
'stringop.c',
|
'stringop.c',
|
||||||
|
'unicode.c',
|
||||||
'util.c'
|
'util.c'
|
||||||
),
|
),
|
||||||
dependencies: deps,
|
dependencies: [
|
||||||
|
cairo,
|
||||||
|
gdk_pixbuf,
|
||||||
|
pango,
|
||||||
|
pangocairo,
|
||||||
|
wlroots
|
||||||
|
],
|
||||||
include_directories: sway_inc
|
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
|
#ifndef _SWAY_INPUT_INPUT_MANAGER_H
|
||||||
#define _SWAY_INPUT_INPUT_MANAGER_H
|
#define _SWAY_INPUT_INPUT_MANAGER_H
|
||||||
#include <libinput.h>
|
#include <libinput.h>
|
||||||
|
#include <wlr/types/wlr_input_inhibitor.h>
|
||||||
#include "sway/server.h"
|
#include "sway/server.h"
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
@ -23,7 +24,11 @@ struct sway_input_manager {
|
||||||
struct wl_list devices;
|
struct wl_list devices;
|
||||||
struct wl_list seats;
|
struct wl_list seats;
|
||||||
|
|
||||||
|
struct wlr_input_inhibit_manager *inhibit;
|
||||||
|
|
||||||
struct wl_listener new_input;
|
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);
|
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
|
// If the focused layer is set, views cannot receive keyboard focus
|
||||||
struct wlr_layer_surface *focused_layer;
|
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 focus_destroy;
|
||||||
struct wl_listener new_container;
|
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,
|
void seat_set_focus_layer(struct sway_seat *seat,
|
||||||
struct wlr_layer_surface *layer);
|
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);
|
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);
|
struct seat_config *seat_get_config(struct sway_seat *seat);
|
||||||
|
|
||||||
|
bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
|
||||||
|
|
||||||
#endif
|
#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
|
#ifndef _SWAYLOCK_H
|
||||||
#define _SWAYLOCK_H
|
#define _SWAYLOCK_H
|
||||||
|
#include <stdbool.h>
|
||||||
#include "client/cairo.h"
|
#include <stdint.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
enum scaling_mode {
|
#include "background-image.h"
|
||||||
SCALING_MODE_STRETCH,
|
#include "cairo.h"
|
||||||
SCALING_MODE_FILL,
|
#include "pool-buffer.h"
|
||||||
SCALING_MODE_FIT,
|
#include "swaylock/seat.h"
|
||||||
SCALING_MODE_CENTER,
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
SCALING_MODE_TILE,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum auth_state {
|
enum auth_state {
|
||||||
AUTH_STATE_IDLE,
|
AUTH_STATE_IDLE,
|
||||||
AUTH_STATE_INPUT,
|
AUTH_STATE_INPUT,
|
||||||
AUTH_STATE_BACKSPACE,
|
AUTH_STATE_BACKSPACE,
|
||||||
AUTH_STATE_VALIDATING,
|
AUTH_STATE_VALIDATING,
|
||||||
AUTH_STATE_INVALID,
|
AUTH_STATE_INVALID,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum line_source {
|
struct swaylock_args {
|
||||||
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;
|
|
||||||
uint32_t color;
|
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;
|
enum auth_state auth_state;
|
||||||
|
bool run_display;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lock_colors {
|
struct swaylock_surface {
|
||||||
uint32_t inner_ring;
|
cairo_surface_t *image;
|
||||||
uint32_t outer_ring;
|
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 {
|
void swaylock_handle_key(struct swaylock_state *state,
|
||||||
char *font;
|
xkb_keysym_t keysym, uint32_t codepoint);
|
||||||
|
void render_frame(struct swaylock_surface *surface);
|
||||||
struct {
|
void render_frames(struct swaylock_state *state);
|
||||||
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);
|
|
||||||
|
|
||||||
#endif
|
#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')
|
pixman = dependency('pixman-1')
|
||||||
libcap = dependency('libcap')
|
libcap = dependency('libcap')
|
||||||
libinput = dependency('libinput')
|
libinput = dependency('libinput')
|
||||||
|
libpam = cc.find_library('libpam')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
rt = cc.find_library('rt')
|
rt = cc.find_library('rt')
|
||||||
git = find_program('git', required: false)
|
git = find_program('git', required: false)
|
||||||
|
@ -105,6 +106,7 @@ subdir('swaymsg')
|
||||||
subdir('client')
|
subdir('client')
|
||||||
subdir('swaybg')
|
subdir('swaybg')
|
||||||
subdir('swaybar')
|
subdir('swaybar')
|
||||||
|
subdir('swaylock')
|
||||||
|
|
||||||
config = configuration_data()
|
config = configuration_data()
|
||||||
config.set('sysconfdir', join_paths(prefix, sysconfdir))
|
config.set('sysconfdir', join_paths(prefix, sysconfdir))
|
||||||
|
|
|
@ -22,12 +22,14 @@ wayland_scanner_server = generator(
|
||||||
|
|
||||||
client_protocols = [
|
client_protocols = [
|
||||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
[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 = [
|
server_protocols = [
|
||||||
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
|
[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 = []
|
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
|
// send pointer enter/leave
|
||||||
if (surface != NULL) {
|
if (surface != NULL) {
|
||||||
wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
|
if (seat_is_input_allowed(cursor->seat, surface)) {
|
||||||
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
|
wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
|
||||||
|
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
wlr_seat_pointer_clear_focus(seat);
|
wlr_seat_pointer_clear_focus(seat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <libinput.h>
|
#include <libinput.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <wlr/backend/libinput.h>
|
#include <wlr/backend/libinput.h>
|
||||||
|
#include <wlr/types/wlr_input_inhibitor.h>
|
||||||
#include "sway/config.h"
|
#include "sway/config.h"
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/seat.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;
|
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_input_manager *input_manager_create(
|
||||||
struct sway_server *server) {
|
struct sway_server *server) {
|
||||||
struct sway_input_manager *input =
|
struct sway_input_manager *input =
|
||||||
|
@ -281,6 +308,14 @@ struct sway_input_manager *input_manager_create(
|
||||||
input->new_input.notify = handle_new_input;
|
input->new_input.notify = handle_new_input;
|
||||||
wl_signal_add(&server->backend->events.new_input, &input->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;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#define _XOPEN_SOURCE 700
|
#define _XOPEN_SOURCE 700
|
||||||
|
#define _POSIX_C_SOURCE 199309L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
|
@ -9,6 +11,7 @@
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/keyboard.h"
|
#include "sway/input/keyboard.h"
|
||||||
#include "sway/ipc-server.h"
|
#include "sway/ipc-server.h"
|
||||||
|
#include "sway/layers.h"
|
||||||
#include "sway/output.h"
|
#include "sway/output.h"
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
#include "sway/tree/view.h"
|
#include "sway/tree/view.h"
|
||||||
|
@ -350,6 +353,12 @@ void seat_configure_xcursor(struct sway_seat *seat) {
|
||||||
seat->cursor->cursor->y);
|
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,
|
void seat_set_focus_warp(struct sway_seat *seat,
|
||||||
struct sway_container *container, bool warp) {
|
struct sway_container *container, bool warp) {
|
||||||
if (seat->focused_layer) {
|
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_remove(&seat_con->link);
|
||||||
wl_list_insert(&seat->focus_stack, &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) {
|
if (container->type == C_VIEW) {
|
||||||
seat_send_focus(seat, container);
|
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,
|
void seat_set_focus_layer(struct sway_seat *seat,
|
||||||
struct wlr_layer_surface *layer) {
|
struct wlr_layer_surface *layer) {
|
||||||
if (!layer) {
|
if (!layer && seat->focused_layer) {
|
||||||
seat->focused_layer = NULL;
|
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;
|
return;
|
||||||
}
|
} else if (!layer || seat->focused_layer == layer) {
|
||||||
if (seat->focused_layer == layer) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (seat->has_focus) {
|
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 *seat_get_focus_inactive(struct sway_seat *seat,
|
||||||
struct sway_container *container) {
|
struct sway_container *container) {
|
||||||
return seat_get_focus_by_type(seat, container, C_TYPES);
|
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 <time.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
#include "background-image.h"
|
||||||
#include "pool-buffer.h"
|
#include "pool-buffer.h"
|
||||||
#include "cairo.h"
|
#include "cairo.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.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 {
|
struct swaybg_args {
|
||||||
int output_idx;
|
int output_idx;
|
||||||
const char *path;
|
const char *path;
|
||||||
|
@ -71,85 +63,18 @@ bool is_valid_color(const char *color) {
|
||||||
return true;
|
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) {
|
static void render_frame(struct swaybg_state *state) {
|
||||||
state->current_buffer = get_next_buffer(state->shm, state->buffers,
|
int buffer_width = state->width * state->scale,
|
||||||
state->width * state->scale, state->height * 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;
|
cairo_t *cairo = state->current_buffer->cairo;
|
||||||
|
if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
|
||||||
switch (state->args->mode) {
|
|
||||||
case BACKGROUND_MODE_SOLID_COLOR:
|
|
||||||
cairo_set_source_u32(cairo, state->context.color);
|
cairo_set_source_u32(cairo, state->context.color);
|
||||||
cairo_paint(cairo);
|
cairo_paint(cairo);
|
||||||
break;
|
} else {
|
||||||
default:
|
render_background_image(cairo, state->context.image,
|
||||||
render_image(state);
|
state->args->mode, buffer_width, buffer_height);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_surface_set_buffer_scale(state->surface, state->scale);
|
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);
|
state->context.color = parse_color(state->args->path);
|
||||||
return is_valid_color(state->args->path);
|
return is_valid_color(state->args->path);
|
||||||
}
|
}
|
||||||
#ifdef HAVE_GDK_PIXBUF
|
if (!(state->context.image = load_background_image(state->args->path))) {
|
||||||
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)));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -294,24 +195,10 @@ int main(int argc, const char **argv) {
|
||||||
args.output_idx = atoi(argv[1]);
|
args.output_idx = atoi(argv[1]);
|
||||||
args.path = argv[2];
|
args.path = argv[2];
|
||||||
|
|
||||||
args.mode = BACKGROUND_MODE_STRETCH;
|
args.mode = parse_background_mode(argv[3]);
|
||||||
if (strcmp(argv[3], "stretch") == 0) {
|
if (args.mode == BACKGROUND_MODE_INVALID) {
|
||||||
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]);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prepare_context(&state)) {
|
if (!prepare_context(&state)) {
|
||||||
return 1;
|
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_set_exclusive_zone(state.layer_surface, -1);
|
||||||
zwlr_layer_surface_v1_add_listener(state.layer_surface,
|
zwlr_layer_surface_v1_add_listener(state.layer_surface,
|
||||||
&layer_surface_listener, &state);
|
&layer_surface_listener, &state);
|
||||||
state.run_display = true;
|
|
||||||
wl_surface_commit(state.surface);
|
wl_surface_commit(state.surface);
|
||||||
wl_display_roundtrip(state.display);
|
wl_display_roundtrip(state.display);
|
||||||
|
|
||||||
|
state.run_display = true;
|
||||||
while (wl_display_dispatch(state.display) != -1 && state.run_display) {
|
while (wl_display_dispatch(state.display) != -1 && state.run_display) {
|
||||||
// This space intentionally left blank
|
// This space intentionally left blank
|
||||||
}
|
}
|
||||||
|
|
904
swaylock/main.c
904
swaylock/main.c
|
@ -1,387 +1,144 @@
|
||||||
#define _XOPEN_SOURCE 500
|
#define _XOPEN_SOURCE 700
|
||||||
#include "wayland-swaylock-client-protocol.h"
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <assert.h>
|
||||||
#include <xkbcommon/xkbcommon-names.h>
|
#include <ctype.h>
|
||||||
#include <security/pam_appl.h>
|
#include <fcntl.h>
|
||||||
#include <json-c/json.h>
|
#include <getopt.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/stat.h>
|
||||||
#include <pwd.h>
|
#include <time.h>
|
||||||
#include <getopt.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "client/window.h"
|
#include <wayland-client.h>
|
||||||
#include "client/registry.h"
|
#include <wlr/util/log.h>
|
||||||
#include "client/cairo.h"
|
#include "swaylock/seat.h"
|
||||||
#include "swaylock/swaylock.h"
|
#include "swaylock/swaylock.h"
|
||||||
#include "ipc-client.h"
|
#include "background-image.h"
|
||||||
#include "log.h"
|
#include "pool-buffer.h"
|
||||||
|
#include "cairo.h"
|
||||||
#include "util.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;
|
static void daemonize() {
|
||||||
struct render_data render_data;
|
if (fork() == 0) {
|
||||||
struct lock_config *config;
|
int devnull = open("/dev/null", O_RDWR);
|
||||||
bool show_indicator = true;
|
dup2(STDOUT_FILENO, devnull);
|
||||||
|
dup2(STDERR_FILENO, devnull);
|
||||||
void wl_dispatch_events() {
|
chdir("/");
|
||||||
wl_display_flush(registry->display);
|
} else {
|
||||||
if (wl_display_dispatch(registry->display) == -1) {
|
exit(0);
|
||||||
sway_log(L_ERROR, "failed to run wl_display_dispatch");
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sigalarm_handler(int sig) {
|
static void layer_surface_configure(void *data,
|
||||||
signal(SIGALRM, SIG_IGN);
|
struct zwlr_layer_surface_v1 *layer_surface,
|
||||||
// Hide typing indicator
|
uint32_t serial, uint32_t width, uint32_t height) {
|
||||||
render_data.auth_state = AUTH_STATE_IDLE;
|
struct swaylock_surface *surface = data;
|
||||||
render(&render_data, config);
|
surface->width = width;
|
||||||
wl_display_flush(registry->display);
|
surface->height = height;
|
||||||
signal(SIGALRM, sigalarm_handler);
|
zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
|
||||||
|
render_frame(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sway_terminate(int exit_code) {
|
static void layer_surface_closed(void *data,
|
||||||
int i;
|
struct zwlr_layer_surface_v1 *layer_surface) {
|
||||||
for (i = 0; i < render_data.surfaces->length; ++i) {
|
struct swaylock_surface *surface = data;
|
||||||
struct window *window = render_data.surfaces->items[i];
|
zwlr_layer_surface_v1_destroy(surface->layer_surface);
|
||||||
window_teardown(window);
|
wl_surface_destroy(surface->surface);
|
||||||
}
|
surface->state->run_display = false;
|
||||||
list_free(render_data.surfaces);
|
|
||||||
if (registry) {
|
|
||||||
registry_teardown(registry);
|
|
||||||
}
|
|
||||||
exit(exit_code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *password;
|
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||||
int password_size;
|
.configure = layer_surface_configure,
|
||||||
enum line_source line_source = LINE_SOURCE_DEFAULT;
|
.closed = layer_surface_closed,
|
||||||
|
};
|
||||||
|
|
||||||
struct lock_config *init_config() {
|
static void output_geometry(void *data, struct wl_output *output, int32_t x,
|
||||||
struct lock_config *config = calloc(1, sizeof(struct lock_config));
|
int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
|
||||||
|
const char *make, const char *model, int32_t transform) {
|
||||||
config->font = strdup("sans-serif");
|
// Who cares
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_config(struct lock_config *config) {
|
static void output_mode(void *data, struct wl_output *output, uint32_t flags,
|
||||||
free(config->font);
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
free(config);
|
// Who cares
|
||||||
}
|
}
|
||||||
|
|
||||||
int function_conversation(int num_msg, const struct pam_message **msg,
|
static void output_done(void *data, struct wl_output *output) {
|
||||||
struct pam_response **resp, void *appdata_ptr) {
|
// Who cares
|
||||||
|
|
||||||
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_scale(void *data, struct wl_output *output, int32_t factor) {
|
||||||
* Note: PAM will free() 'password' during the process
|
struct swaylock_surface *surface = data;
|
||||||
*/
|
surface->scale = factor;
|
||||||
bool verify_password() {
|
if (surface->state->run_display) {
|
||||||
struct passwd *passwd = getpwuid(getuid());
|
render_frames(surface->state);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_color(struct window *window, uint32_t color) {
|
struct wl_output_listener output_listener = {
|
||||||
cairo_set_source_u32(window->cairo, color);
|
.geometry = output_geometry,
|
||||||
cairo_paint(window->cairo);
|
.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) {
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
double width = cairo_image_surface_get_width(image);
|
uint32_t name) {
|
||||||
double height = cairo_image_surface_get_height(image);
|
// who cares
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_surface_t *load_image(char *image_path) {
|
static const struct wl_registry_listener registry_listener = {
|
||||||
cairo_surface_t *image = NULL;
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef WITH_GDK_PIXBUF
|
static struct swaylock_state state;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
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[] = {
|
static struct option long_options[] = {
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"color", required_argument, NULL, 'c'},
|
{"color", required_argument, NULL, 'c'},
|
||||||
{"image", required_argument, NULL, 'i'},
|
{"image", required_argument, NULL, 'i'},
|
||||||
{"scaling", required_argument, NULL, 0},
|
{"scaling", required_argument, NULL, 's'},
|
||||||
{"tiling", no_argument, NULL, 't'},
|
{"tiling", no_argument, NULL, 't'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
{"socket", required_argument, NULL, 'p'},
|
{"socket", required_argument, NULL, 'p'},
|
||||||
{"no-unlock-indicator", no_argument, NULL, 'u'},
|
{"no-unlock-indicator", no_argument, NULL, 'u'},
|
||||||
{"daemonize", no_argument, NULL, 'f'},
|
{"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}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -390,415 +147,124 @@ int main(int argc, char **argv) {
|
||||||
"\n"
|
"\n"
|
||||||
" -h, --help Show help message and quit.\n"
|
" -h, --help Show help message and quit.\n"
|
||||||
" -c, --color <rrggbb[aa]> Turn the screen into the given color instead of white.\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"
|
" -t, --tiling Same as --scaling=tile.\n"
|
||||||
" -v, --version Show the version number and quit.\n"
|
" -v, --version Show the version number and quit.\n"
|
||||||
" -i, --image [<output>:]<path> Display the given image.\n"
|
" -i, --image [<output>:]<path> Display the given image.\n"
|
||||||
" -u, --no-unlock-indicator Disable the unlock indicator.\n"
|
" -u, --no-unlock-indicator Disable the unlock indicator.\n"
|
||||||
" -f, --daemonize Detach from the controlling terminal.\n"
|
" -f, --daemonize Detach from the controlling terminal.\n"
|
||||||
" --socket <socket> Use the specified socket.\n"
|
" --socket <socket> Use the specified socket.\n";
|
||||||
" For more information see `man swaylock`\n";
|
|
||||||
|
|
||||||
|
struct swaylock_args args = {
|
||||||
registry = registry_poll();
|
.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;
|
int c;
|
||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
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) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c': {
|
||||||
{
|
state.args.color = parse_color(optarg);
|
||||||
render_data.color = parse_color(optarg);
|
state.args.mode = BACKGROUND_MODE_SOLID_COLOR;
|
||||||
render_data.color_set = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
// TODO: Multiple background images (bleh)
|
||||||
char *image_path = strchr(optarg, ':');
|
background_image = load_background_image(optarg);
|
||||||
if (image_path == NULL) {
|
if (!background_image) {
|
||||||
if (render_data.num_images == 0) {
|
return 1;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
break;
|
state.args.mode = BACKGROUND_MODE_FILL;
|
||||||
}
|
|
||||||
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;
|
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (line_source != LINE_SOURCE_DEFAULT) {
|
state.args.mode = parse_background_mode(optarg);
|
||||||
sway_log(L_ERROR, "line source options conflict");
|
if (state.args.mode == BACKGROUND_MODE_INVALID) {
|
||||||
exit(EXIT_FAILURE);
|
return 1;
|
||||||
}
|
}
|
||||||
line_source = LINE_SOURCE_INSIDE;
|
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 't':
|
||||||
if (strcmp(long_options[option_index].name, "font") == 0) {
|
state.args.mode = BACKGROUND_MODE_TILE;
|
||||||
free(config->font);
|
break;
|
||||||
config->font = strdup(optarg);
|
case 'v':
|
||||||
} else if (strcmp(long_options[option_index].name, "scaling") == 0) {
|
#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
|
||||||
scaling_mode_str = optarg;
|
fprintf(stdout, "swaylock version %s (%s, branch \"%s\")\n",
|
||||||
} else if (strcmp(long_options[option_index].name, "textcolor") == 0) {
|
SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH);
|
||||||
config->colors.text = parse_color(optarg);
|
#else
|
||||||
} else if (strcmp(long_options[option_index].name, "insidevercolor") == 0) {
|
fprintf(stdout, "version unknown\n");
|
||||||
config->colors.validating.inner_ring = parse_color(optarg);
|
#endif
|
||||||
} else if (strcmp(long_options[option_index].name, "insidewrongcolor") == 0) {
|
return 0;
|
||||||
config->colors.invalid.inner_ring = parse_color(optarg);
|
case 'u':
|
||||||
} else if (strcmp(long_options[option_index].name, "insidecolor") == 0) {
|
state.args.show_indicator = false;
|
||||||
config->colors.normal.inner_ring = parse_color(optarg);
|
break;
|
||||||
} else if (strcmp(long_options[option_index].name, "ringvercolor") == 0) {
|
case 'f':
|
||||||
config->colors.validating.outer_ring = parse_color(optarg);
|
daemonize();
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "%s", usage);
|
fprintf(stderr, "%s", usage);
|
||||||
exit(EXIT_FAILURE);
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_data.scaling_mode = SCALING_MODE_STRETCH;
|
wl_list_init(&state.surfaces);
|
||||||
if (strcmp(scaling_mode_str, "stretch") == 0) {
|
state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
render_data.scaling_mode = SCALING_MODE_STRETCH;
|
state.display = wl_display_connect(NULL);
|
||||||
} else if (strcmp(scaling_mode_str, "fill") == 0) {
|
assert(state.display);
|
||||||
render_data.scaling_mode = SCALING_MODE_FILL;
|
|
||||||
} else if (strcmp(scaling_mode_str, "fit") == 0) {
|
struct wl_registry *registry = wl_display_get_registry(state.display);
|
||||||
render_data.scaling_mode = SCALING_MODE_FIT;
|
wl_registry_add_listener(registry, ®istry_listener, &state);
|
||||||
} else if (strcmp(scaling_mode_str, "center") == 0) {
|
wl_display_roundtrip(state.display);
|
||||||
render_data.scaling_mode = SCALING_MODE_CENTER;
|
assert(state.compositor && state.layer_shell && state.shm);
|
||||||
} else if (strcmp(scaling_mode_str, "tile") == 0) {
|
if (!state.input_inhibit_manager) {
|
||||||
render_data.scaling_mode = SCALING_MODE_TILE;
|
wlr_log(L_ERROR, "Compositor does not support the input inhibitor "
|
||||||
} else {
|
"protocol, refusing to run insecurely");
|
||||||
sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
password_size = 1024;
|
if (wl_list_empty(&state.surfaces)) {
|
||||||
password = malloc(password_size);
|
wlr_log(L_DEBUG, "Exiting - no outputs to show on.");
|
||||||
password[0] = '\0';
|
return 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) {
|
struct swaylock_surface *surface;
|
||||||
sway_abort("Unable to connect to wayland compositor");
|
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) {
|
zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
|
||||||
sway_abort("swaylock requires the compositor to support the swaylock extension.");
|
|
||||||
|
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;
|
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