Merge branch 'wlroots' into fix-focus-inactive

This commit is contained in:
Tony Crisci 2018-04-04 22:36:09 -04:00
commit 65f254f3fb
33 changed files with 1357 additions and 995 deletions

119
common/background-image.c Normal file
View 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);
}

View file

@ -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
View 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;
}

View 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

View file

@ -123,6 +123,7 @@ sway_cmd cmd_mark;
sway_cmd cmd_mode; sway_cmd cmd_mode;
sway_cmd cmd_mouse_warping; sway_cmd cmd_mouse_warping;
sway_cmd cmd_move; sway_cmd cmd_move;
sway_cmd cmd_opacity;
sway_cmd cmd_new_float; sway_cmd cmd_new_float;
sway_cmd cmd_new_window; sway_cmd cmd_new_window;
sway_cmd cmd_no_focus; sway_cmd cmd_no_focus;

View file

@ -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);

View file

@ -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

View file

@ -83,6 +83,8 @@ struct sway_container {
list_t *marks; // list of char* list_t *marks; // list of char*
float alpha;
struct { struct {
struct wl_signal destroy; struct wl_signal destroy;
// Raised after the tree updates, but before arrange_windows // Raised after the tree updates, but before arrange_windows

View file

@ -59,11 +59,9 @@ struct sway_wl_shell_surface {
}; };
enum sway_view_type { enum sway_view_type {
SWAY_WL_SHELL_VIEW, SWAY_VIEW_WL_SHELL,
SWAY_XDG_SHELL_V6_VIEW, SWAY_VIEW_XDG_SHELL_V6,
SWAY_XWAYLAND_VIEW, SWAY_VIEW_XWAYLAND,
// Keep last
SWAY_VIEW_TYPES,
}; };
enum sway_view_prop { enum sway_view_prop {
@ -101,9 +99,6 @@ struct sway_view {
struct sway_xwayland_surface *sway_xwayland_surface; struct sway_xwayland_surface *sway_xwayland_surface;
struct sway_wl_shell_surface *sway_wl_shell_surface; struct sway_wl_shell_surface *sway_wl_shell_surface;
}; };
// only used for unmanaged views (shell specific)
struct wl_list unmanaged_view_link; // sway_root::unmanaged_views
}; };
struct sway_view *view_create(enum sway_view_type type, struct sway_view *view_create(enum sway_view_type type,

38
include/swaylock/seat.h Normal file
View 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

View file

@ -1,15 +1,13 @@
#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,
@ -19,48 +17,48 @@ enum auth_state {
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
View 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

View file

@ -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))

View file

@ -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 = []

View 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>

View file

@ -163,6 +163,7 @@ static struct cmd_handler command_handlers[] = {
{ "kill", cmd_kill }, { "kill", cmd_kill },
{ "layout", cmd_layout }, { "layout", cmd_layout },
{ "move", cmd_move }, { "move", cmd_move },
{ "opacity", cmd_opacity },
{ "reload", cmd_reload }, { "reload", cmd_reload },
{ "split", cmd_split }, { "split", cmd_split },
{ "splith", cmd_splith }, { "splith", cmd_splith },

39
sway/commands/opacity.c Normal file
View file

@ -0,0 +1,39 @@
#include <assert.h>
#include <stdlib.h>
#include "sway/commands.h"
#include "sway/tree/view.h"
#include "log.h"
static bool parse_opacity(const char *opacity, float *val) {
char *err;
*val = strtof(opacity, &err);
if (*val < 0 || *val > 1 || *err) {
return false;
}
return true;
}
struct cmd_results *cmd_opacity(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_EQUAL_TO, 1))) {
return error;
}
struct sway_container *con =
config->handler_context.current_container;
float opacity = 0.0f;
if (!parse_opacity(argv[0], &opacity)) {
return cmd_results_new(CMD_INVALID, "opacity <value>",
"Invalid value (expected 0..1): %s", argv[0]);
}
con->alpha = opacity;
if (con->type == C_VIEW) {
view_damage_whole(con->sway_view);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -75,7 +75,7 @@ static bool surface_intersect_output(struct wlr_surface *surface,
static void render_surface(struct wlr_surface *surface, static void render_surface(struct wlr_surface *surface,
struct wlr_output *wlr_output, struct timespec *when, struct wlr_output *wlr_output, struct timespec *when,
double ox, double oy, float rotation) { double ox, double oy, float rotation, float alpha) {
struct wlr_renderer *renderer = struct wlr_renderer *renderer =
wlr_backend_get_renderer(wlr_output->backend); wlr_backend_get_renderer(wlr_output->backend);
@ -95,8 +95,8 @@ static void render_surface(struct wlr_surface *surface,
wlr_matrix_project_box(matrix, &box, transform, rotation, wlr_matrix_project_box(matrix, &box, transform, rotation,
wlr_output->transform_matrix); wlr_output->transform_matrix);
// TODO: configurable alpha wlr_render_texture_with_matrix(renderer, surface->texture,
wlr_render_texture_with_matrix(renderer, surface->texture, matrix, 1.0f); matrix, alpha);
wlr_surface_send_frame_done(surface, when); wlr_surface_send_frame_done(surface, when);
} }
@ -110,13 +110,13 @@ static void render_surface(struct wlr_surface *surface,
surface->current->width, surface->current->height, rotation); surface->current->width, surface->current->height, rotation);
render_surface(subsurface->surface, wlr_output, when, render_surface(subsurface->surface, wlr_output, when,
ox + sx, oy + sy, rotation); ox + sx, oy + sy, rotation, alpha);
} }
} }
static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
struct wlr_output *wlr_output, struct timespec *when, double base_x, struct wlr_output *wlr_output, struct timespec *when, double base_x,
double base_y, float rotation) { double base_y, float rotation, float alpha) {
double width = surface->surface->current->width; double width = surface->surface->current->width;
double height = surface->surface->current->height; double height = surface->surface->current->height;
@ -136,19 +136,19 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
width, height, rotation); width, height, rotation);
render_surface(popup->surface, wlr_output, when, render_surface(popup->surface, wlr_output, when,
base_x + popup_sx, base_y + popup_sy, rotation); base_x + popup_sx, base_y + popup_sy, rotation, alpha);
render_xdg_v6_popups(popup, wlr_output, when, render_xdg_v6_popups(popup, wlr_output, when,
base_x + popup_sx, base_y + popup_sy, rotation); base_x + popup_sx, base_y + popup_sy, rotation, alpha);
} }
} }
static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface,
struct wlr_output *wlr_output, struct timespec *when, struct wlr_output *wlr_output, struct timespec *when,
double lx, double ly, float rotation, double lx, double ly, float rotation, float alpha,
bool is_child) { bool is_child) {
if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) {
render_surface(surface->surface, wlr_output, when, render_surface(surface->surface, wlr_output, when,
lx, ly, rotation); lx, ly, rotation, alpha);
double width = surface->surface->current->width; double width = surface->surface->current->width;
double height = surface->surface->current->height; double height = surface->surface->current->height;
@ -164,7 +164,7 @@ static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface,
width, height, rotation); width, height, rotation);
render_wl_shell_surface(popup, wlr_output, when, render_wl_shell_surface(popup, wlr_output, when,
lx + popup_x, ly + popup_y, rotation, true); lx + popup_x, ly + popup_y, rotation, alpha, true);
} }
} }
} }
@ -181,29 +181,28 @@ static void render_view(struct sway_container *view, void *data) {
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
struct sway_view *sway_view = view->sway_view; struct sway_view *sway_view = view->sway_view;
struct wlr_surface *surface = sway_view->surface; struct wlr_surface *surface = sway_view->surface;
float alpha = sway_view->swayc->alpha;
if (!surface) { if (!surface) {
return; return;
} }
switch (sway_view->type) { switch (sway_view->type) {
case SWAY_XDG_SHELL_V6_VIEW: { case SWAY_VIEW_XDG_SHELL_V6: {
int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x; int window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry.x;
int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y; int window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry.y;
render_surface(surface, wlr_output, when, render_surface(surface, wlr_output, when,
view->x - window_offset_x, view->y - window_offset_y, 0); view->x - window_offset_x, view->y - window_offset_y, 0, alpha);
render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output, render_xdg_v6_popups(sway_view->wlr_xdg_surface_v6, wlr_output,
when, view->x - window_offset_x, view->y - window_offset_y, 0); when, view->x - window_offset_x, view->y - window_offset_y, 0, alpha);
break; break;
} }
case SWAY_WL_SHELL_VIEW: case SWAY_VIEW_WL_SHELL:
render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output, render_wl_shell_surface(sway_view->wlr_wl_shell_surface, wlr_output,
when, view->x, view->y, 0, false); when, view->x, view->y, 0, alpha, false);
break; break;
case SWAY_XWAYLAND_VIEW: case SWAY_VIEW_XWAYLAND:
render_surface(surface, wlr_output, when, view->x, view->y, 0); render_surface(surface, wlr_output, when, view->x, view->y, 0, alpha);
break;
default:
break; break;
} }
} }
@ -214,7 +213,7 @@ static void render_layer(struct sway_output *output, struct timespec *when,
wl_list_for_each(sway_layer, layer, link) { wl_list_for_each(sway_layer, layer, link) {
struct wlr_layer_surface *layer = sway_layer->layer_surface; struct wlr_layer_surface *layer = sway_layer->layer_surface;
render_surface(layer->surface, output->wlr_output, when, render_surface(layer->surface, output->wlr_output, when,
sway_layer->geo.x, sway_layer->geo.y, 0); sway_layer->geo.x, sway_layer->geo.y, 0, 1.0f);
wlr_surface_send_frame_done(layer->surface, when); wlr_surface_send_frame_done(layer->surface, when);
} }
} }
@ -288,7 +287,7 @@ static void render_output(struct sway_output *output, struct timespec *when,
} }
render_surface(xsurface->surface, wlr_output, &output->last_frame, render_surface(xsurface->surface, wlr_output, &output->last_frame,
view_box.x - output_box->x, view_box.y - output_box->y, 0); view_box.x - output_box->x, view_box.y - output_box->y, 0, 1.0f);
} }
// TODO: Consider revising this when fullscreen windows are supported // TODO: Consider revising this when fullscreen windows are supported

View file

@ -12,7 +12,7 @@
#include "log.h" #include "log.h"
static bool assert_wl_shell(struct sway_view *view) { static bool assert_wl_shell(struct sway_view *view) {
return sway_assert(view->type == SWAY_WL_SHELL_VIEW, return sway_assert(view->type == SWAY_VIEW_WL_SHELL,
"Expecting wl_shell view!"); "Expecting wl_shell view!");
} }
@ -97,7 +97,7 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
return; return;
} }
struct sway_view *view = view_create(SWAY_WL_SHELL_VIEW, &view_impl); struct sway_view *view = view_create(SWAY_VIEW_WL_SHELL, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) { if (!sway_assert(view, "Failed to allocate view")) {
return; return;
} }

View file

@ -12,7 +12,7 @@
#include "log.h" #include "log.h"
static bool assert_xdg(struct sway_view *view) { static bool assert_xdg(struct sway_view *view) {
return sway_assert(view->type == SWAY_XDG_SHELL_V6_VIEW, return sway_assert(view->type == SWAY_VIEW_XDG_SHELL_V6,
"Expected xdg shell v6 view!"); "Expected xdg shell v6 view!");
} }
@ -126,7 +126,7 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
return; return;
} }
struct sway_view *view = view_create(SWAY_XDG_SHELL_V6_VIEW, &view_impl); struct sway_view *view = view_create(SWAY_VIEW_XDG_SHELL_V6, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) { if (!sway_assert(view, "Failed to allocate view")) {
return; return;
} }

View file

@ -42,7 +42,7 @@ static void create_unmanaged(struct wlr_xwayland_surface *xsurface) {
static bool assert_xwayland(struct sway_view *view) { static bool assert_xwayland(struct sway_view *view) {
return sway_assert(view->type == SWAY_XWAYLAND_VIEW, return sway_assert(view->type == SWAY_VIEW_XWAYLAND,
"Expected xwayland view!"); "Expected xwayland view!");
} }
@ -185,7 +185,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
return; return;
} }
struct sway_view *view = view_create(SWAY_XWAYLAND_VIEW, &view_impl); struct sway_view *view = view_create(SWAY_VIEW_XWAYLAND, &view_impl);
if (!sway_assert(view, "Failed to allocate view")) { if (!sway_assert(view, "Failed to allocate view")) {
return; return;
} }

View file

@ -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) {
if (seat_is_input_allowed(cursor->seat, surface)) {
wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat, time, 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);
} }

View file

@ -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;
} }

View file

@ -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"
@ -63,7 +66,7 @@ static void seat_send_focus(struct sway_seat *seat,
return; return;
} }
struct sway_view *view = con->sway_view; struct sway_view *view = con->sway_view;
if (view->type == SWAY_XWAYLAND_VIEW) { if (view->type == SWAY_VIEW_XWAYLAND) {
struct wlr_xwayland *xwayland = struct wlr_xwayland *xwayland =
seat->input->server->xwayland; seat->input->server->xwayland;
wlr_xwayland_set_seat(xwayland, seat->wlr_seat); wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@ -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;
return; 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);
} }
if (seat->focused_layer == layer) { return;
} else if (!layer || 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);

View file

@ -15,6 +15,7 @@ sway_sources = files(
'commands/focus.c', 'commands/focus.c',
'commands/focus_follows_mouse.c', 'commands/focus_follows_mouse.c',
'commands/kill.c', 'commands/kill.c',
'commands/opacity.c',
'commands/include.c', 'commands/include.c',
'commands/input.c', 'commands/input.c',
'commands/layout.c', 'commands/layout.c',

View file

@ -413,6 +413,10 @@ The default colors are:
However, any mark that starts with an underscore will not be drawn even if the However, any mark that starts with an underscore will not be drawn even if the
option is on. The default option is _on_. option is on. The default option is _on_.
**opacity** <value>::
Set the opacity of the window between 0 (completely transparent) and 1
(completely opaque).
**unmark** <identifier>:: **unmark** <identifier>::
**Unmark** will remove _identifier_ from the list of current marks on a window. If **Unmark** will remove _identifier_ from the list of current marks on a window. If
no _identifier_ is specified, then **unmark** will remove all marks. no _identifier_ is specified, then **unmark** will remove all marks.

View file

@ -78,6 +78,8 @@ struct sway_container *container_create(enum sway_container_type type) {
c->layout = L_NONE; c->layout = L_NONE;
c->workspace_layout = L_NONE; c->workspace_layout = L_NONE;
c->type = type; c->type = type;
c->alpha = 1.0f;
if (type != C_VIEW) { if (type != C_VIEW) {
c->children = create_list(); c->children = create_list();
} }
@ -416,51 +418,33 @@ struct sway_container *container_at(struct sway_container *parent,
double view_sx = ox - swayc->x; double view_sx = ox - swayc->x;
double view_sy = oy - swayc->y; double view_sy = oy - swayc->y;
double _sx, _sy;
struct wlr_surface *_surface;
switch (sview->type) { switch (sview->type) {
case SWAY_WL_SHELL_VIEW: case SWAY_VIEW_XWAYLAND:
_surface = wlr_surface_surface_at(sview->surface,
view_sx, view_sy, &_sx, &_sy);
break; break;
case SWAY_XDG_SHELL_V6_VIEW: case SWAY_VIEW_WL_SHELL:
_surface = wlr_wl_shell_surface_surface_at(
sview->wlr_wl_shell_surface,
view_sx, view_sy, &_sx, &_sy);
break;
case SWAY_VIEW_XDG_SHELL_V6:
// the top left corner of the sway container is the // the top left corner of the sway container is the
// coordinate of the top left corner of the window geometry // coordinate of the top left corner of the window geometry
view_sx += sview->wlr_xdg_surface_v6->geometry.x; view_sx += sview->wlr_xdg_surface_v6->geometry.x;
view_sy += sview->wlr_xdg_surface_v6->geometry.y; view_sy += sview->wlr_xdg_surface_v6->geometry.y;
// check for popups _surface = wlr_xdg_surface_v6_surface_at(
double popup_sx, popup_sy; sview->wlr_xdg_surface_v6,
struct wlr_xdg_surface_v6 *popup = view_sx, view_sy, &_sx, &_sy);
wlr_xdg_surface_v6_popup_at(sview->wlr_xdg_surface_v6,
view_sx, view_sy, &popup_sx, &popup_sy);
if (popup) {
*sx = view_sx - popup_sx;
*sy = view_sy - popup_sy;
*surface = popup->surface;
return swayc;
}
break;
case SWAY_XWAYLAND_VIEW:
break;
default:
break; break;
} }
if (_surface) {
// check for subsurfaces *sx = _sx;
double sub_x, sub_y; *sy = _sy;
struct wlr_subsurface *subsurface = *surface = _surface;
wlr_surface_subsurface_at(sview->surface,
view_sx, view_sy, &sub_x, &sub_y);
if (subsurface) {
*sx = view_sx - sub_x;
*sy = view_sy - sub_y;
*surface = subsurface->surface;
return swayc;
}
if (wlr_surface_point_accepts_input(
sview->surface, view_sx, view_sy)) {
*sx = view_sx;
*sy = view_sy;
*surface = swayc->sway_view->surface;
return swayc; return swayc;
} }
} else { } else {

View file

@ -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
} }

View file

@ -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) {
sway_log(L_ERROR, "failed to run wl_display_dispatch");
exit(1);
}
}
void sigalarm_handler(int sig) {
signal(SIGALRM, SIG_IGN);
// Hide typing indicator
render_data.auth_state = AUTH_STATE_IDLE;
render(&render_data, config);
wl_display_flush(registry->display);
signal(SIGALRM, sigalarm_handler);
}
void sway_terminate(int exit_code) {
int i;
for (i = 0; i < render_data.surfaces->length; ++i) {
struct window *window = render_data.surfaces->items[i];
window_teardown(window);
}
list_free(render_data.surfaces);
if (registry) {
registry_teardown(registry);
}
exit(exit_code);
}
char *password;
int password_size;
enum line_source line_source = LINE_SOURCE_DEFAULT;
struct lock_config *init_config() {
struct lock_config *config = calloc(1, sizeof(struct lock_config));
config->font = strdup("sans-serif");
config->colors.text = 0x000000FF;
config->colors.line = 0x000000FF;
config->colors.separator = 0x000000FF;
config->colors.input_cursor = 0x33DB00FF;
config->colors.backspace_cursor = 0xDB3300FF;
config->colors.normal.inner_ring = 0x000000BF;
config->colors.normal.outer_ring = 0x337D00FF;
config->colors.validating.inner_ring = 0x0072FFBF;
config->colors.validating.outer_ring = 0x3300FAFF;
config->colors.invalid.inner_ring = 0xFA0000BF;
config->colors.invalid.outer_ring = 0x7D3300FF;
config->radius = 50;
config->thickness = 10;
return config;
}
void free_config(struct lock_config *config) {
free(config->font);
free(config);
}
int function_conversation(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr) {
const char* msg_style_names[] = {
NULL,
"PAM_PROMPT_ECHO_OFF",
"PAM_PROMPT_ECHO_ON",
"PAM_ERROR_MSG",
"PAM_TEXT_INFO",
};
/* PAM expects an array of responses, one for each message */
struct pam_response *pam_reply = calloc(num_msg, sizeof(struct pam_response));
*resp = pam_reply;
for(int i=0; i<num_msg; ++i) {
sway_log(L_DEBUG, "msg[%d]: (%s) %s", i,
msg_style_names[msg[i]->msg_style],
msg[i]->msg);
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
pam_reply[i].resp = password;
break;
case PAM_ERROR_MSG:
case PAM_TEXT_INFO:
break;
}
}
return PAM_SUCCESS;
}
/**
* Note: PAM will free() 'password' during the process
*/
bool verify_password() {
struct passwd *passwd = getpwuid(getuid());
char *username = passwd->pw_name;
const struct pam_conv local_conversation = { function_conversation, NULL };
pam_handle_t *local_auth_handle = NULL;
int pam_err;
if ((pam_err = pam_start("swaylock", username, &local_conversation, &local_auth_handle)) != PAM_SUCCESS) {
sway_abort("PAM returned %d\n", pam_err);
}
if ((pam_err = pam_authenticate(local_auth_handle, 0)) != PAM_SUCCESS) {
return false;
}
if ((pam_err = pam_end(local_auth_handle, pam_err)) != PAM_SUCCESS) {
return false;
}
return true;
}
void notify_key(enum wl_keyboard_key_state state, xkb_keysym_t sym, uint32_t code, uint32_t codepoint) {
int redraw_screen = 0;
char *password_realloc;
int i;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
switch (sym) {
case XKB_KEY_KP_Enter:
case XKB_KEY_Return:
render_data.auth_state = AUTH_STATE_VALIDATING;
render(&render_data, config);
// Make sure our render call will actually be displayed on the screen
wl_dispatch_events();
if (verify_password()) {
exit(0); exit(0);
} }
}
render_data.auth_state = AUTH_STATE_INVALID; static void layer_surface_configure(void *data,
redraw_screen = 1; struct zwlr_layer_surface_v1 *layer_surface,
uint32_t serial, uint32_t width, uint32_t height) {
struct swaylock_surface *surface = data;
surface->width = width;
surface->height = height;
zwlr_layer_surface_v1_ack_configure(layer_surface, serial);
render_frame(surface);
}
password_size = 1024; static void layer_surface_closed(void *data,
password = malloc(password_size); struct zwlr_layer_surface_v1 *layer_surface) {
password[0] = '\0'; struct swaylock_surface *surface = data;
break; zwlr_layer_surface_v1_destroy(surface->layer_surface);
case XKB_KEY_BackSpace: wl_surface_destroy(surface->surface);
i = strlen(password); surface->state->run_display = false;
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; static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
free(password); .configure = layer_surface_configure,
password = malloc(password_size); .closed = layer_surface_closed,
password[0] = '\0'; };
break;
static void output_geometry(void *data, struct wl_output *output, int32_t x,
int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
const char *make, const char *model, int32_t transform) {
// Who cares
} }
/* fallthrough */
default: static void output_mode(void *data, struct wl_output *output, uint32_t flags,
render_data.auth_state = AUTH_STATE_INPUT; int32_t width, int32_t height, int32_t refresh) {
redraw_screen = 1; // Who cares
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;
} }
static void output_done(void *data, struct wl_output *output) {
// Who cares
} }
password[i] = (char)codepoint;
password[i + 1] = '\0'; static void output_scale(void *data, struct wl_output *output, int32_t factor) {
break; struct swaylock_surface *surface = data;
} surface->scale = factor;
if (redraw_screen) { if (surface->state->run_display) {
render(&render_data, config); render_frames(surface->state);
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,
};
void render_image(struct window *window, cairo_surface_t *image, enum scaling_mode scaling_mode) { static void handle_global(void *data, struct wl_registry *registry,
double width = cairo_image_surface_get_width(image); uint32_t name, const char *interface, uint32_t version) {
double height = cairo_image_surface_get_height(image); struct swaylock_state *state = data;
int wwidth = window->width * window->scale; if (strcmp(interface, wl_compositor_interface.name) == 0) {
int wheight = window->height * window->scale; state->compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 3);
switch (scaling_mode) { } else if (strcmp(interface, wl_shm_interface.name) == 0) {
case SCALING_MODE_STRETCH: state->shm = wl_registry_bind(registry, name,
cairo_scale(window->cairo, &wl_shm_interface, 1);
(double) wwidth / width, } else if (strcmp(interface, wl_seat_interface.name) == 0) {
(double) wheight / height); struct wl_seat *seat = wl_registry_bind(
cairo_set_source_surface(window->cairo, image, 0, 0); registry, name, &wl_seat_interface, 1);
break; wl_seat_add_listener(seat, &seat_listener, state);
case SCALING_MODE_FILL: } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
{ state->layer_shell = wl_registry_bind(
double window_ratio = (double) wwidth / wheight; registry, name, &zwlr_layer_shell_v1_interface, 1);
double bg_ratio = width / height; } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
state->input_inhibit_manager = wl_registry_bind(
if (window_ratio > bg_ratio) { registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
double scale = (double) wwidth / width; } else if (strcmp(interface, wl_output_interface.name) == 0) {
cairo_scale(window->cairo, scale, scale); struct swaylock_surface *surface =
cairo_set_source_surface(window->cairo, image, calloc(1, sizeof(struct swaylock_surface));
0, surface->state = state;
(double) wheight/2 / scale - height/2); surface->output = wl_registry_bind(registry, name,
} else { &wl_output_interface, 3);
double scale = (double) wheight / height; wl_output_add_listener(surface->output, &output_listener, surface);
cairo_scale(window->cairo, scale, scale); wl_list_insert(&state->surfaces, &surface->link);
cairo_set_source_surface(window->cairo, image,
(double) wwidth/2 / scale - width/2,
0);
}
break;
}
case SCALING_MODE_FIT:
{
double window_ratio = (double) wwidth / wheight;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) wheight / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) wwidth/2 / scale - width/2,
0);
} else {
double scale = (double) wwidth / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) wheight/2 / scale - height/2);
}
break;
}
case SCALING_MODE_CENTER:
cairo_set_source_surface(window->cairo, image,
(double) wwidth/2 - width/2,
(double) wheight/2 - height/2);
break;
case SCALING_MODE_TILE:
{
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(window->cairo, pattern);
break;
} }
} }
cairo_paint(window->cairo); static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {
// who cares
} }
cairo_surface_t *load_image(char *image_path) { 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 { state.args.mode = BACKGROUND_MODE_FILL;
// 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;
}
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, &registry_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';
render_data.surfaces = create_list();
if (!socket_path) {
socket_path = get_socketpath();
if (!socket_path) {
sway_abort("Unable to retrieve socket path");
}
}
if (!registry) {
sway_abort("Unable to connect to wayland compositor");
}
if (!registry->swaylock) {
sway_abort("swaylock requires the compositor to support the swaylock extension.");
}
if (registry->pointer) {
// We don't want swaylock to have a pointer
wl_pointer_destroy(registry->pointer);
registry->pointer = NULL;
}
for (i = 0; i < registry->outputs->length; ++i) {
struct output_state *output = registry->outputs->items[i];
struct window *window = window_setup(registry,
output->width, output->height, output->scale, true);
if (!window) {
sway_abort("Failed to create surfaces.");
}
list_add(render_data.surfaces, window);
}
registry->input->notify = notify_key;
// Different background for the output
if (render_data.num_images >= 1) {
char **displays_paths = images;
render_data.images = calloc(registry->outputs->length, sizeof(cairo_surface_t*));
int socketfd = ipc_open_socket(socket_path);
uint32_t len = 0;
char *outputs = ipc_single_command(socketfd, IPC_GET_OUTPUTS, "", &len);
struct json_object *json_outputs = json_tokener_parse(outputs);
for (i = 0; i < registry->outputs->length; ++i) {
if (displays_paths[i * 2] != NULL) {
for (int j = 0;; ++j) {
if (j >= json_object_array_length(json_outputs)) {
sway_log(L_ERROR, "%s is not an extant output", displays_paths[i * 2]);
exit(EXIT_FAILURE);
}
struct json_object *dsp_name, *at_j = json_object_array_get_idx(json_outputs, j);
if (!json_object_object_get_ex(at_j, "name", &dsp_name)) {
sway_abort("output doesn't have a name field");
}
if (!strcmp(displays_paths[i * 2], json_object_get_string(dsp_name))) {
render_data.images[j] = load_image(displays_paths[i * 2 + 1]);
break;
}
}
}
}
json_object_put(json_outputs);
close(socketfd);
free(displays_paths);
}
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) { struct swaylock_surface *surface;
int i; wl_list_for_each(surface, &state.surfaces, link) {
for (i = 0; i < render_data->surfaces->length; ++i) { surface->image = background_image;
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); surface->surface = wl_compositor_create_surface(state.compositor);
cairo_set_operator(window->cairo, CAIRO_OPERATOR_CLEAR); assert(surface->surface);
cairo_paint(window->cairo);
cairo_restore(window->cairo);
// Reset the transformation matrix surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
cairo_identity_matrix(window->cairo); state.layer_shell, surface->surface, surface->output,
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen");
assert(surface->layer_surface);
if (render_data->num_images == 0 || render_data->color_set) { zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0);
render_color(window, render_data->color); 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 (render_data->num_images == -1) { zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
// 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 state.run_display = true;
cairo_identity_matrix(window->cairo); while (wl_display_dispatch(state.display) != -1 && state.run_display) {
// This space intentionally left blank
// 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);
} }
return 0;
} }

23
swaylock/meson.build Normal file
View 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
View 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
View 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
View 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,
};