add seat sub command 'xcursor_theme'

New 'seat <name> xcursor_theme <theme> [<size>]' command that
configures the default xcursor theme.

The default seat's xcursor theme is also propagated to XWayland, and
exported through the XCURSOR_THEME and XCURSOR_SIZE environment
variables. This is done every time the default seat's configuration is
changed.
This commit is contained in:
Daniel Eklöf 2019-06-01 21:05:09 +02:00 committed by Drew DeVault
parent 799f5a2cd5
commit 190546fd31
9 changed files with 119 additions and 29 deletions

View file

@ -278,6 +278,7 @@ sway_cmd seat_cmd_cursor;
sway_cmd seat_cmd_fallback; sway_cmd seat_cmd_fallback;
sway_cmd seat_cmd_hide_cursor; sway_cmd seat_cmd_hide_cursor;
sway_cmd seat_cmd_pointer_constraint; sway_cmd seat_cmd_pointer_constraint;
sway_cmd seat_cmd_xcursor_theme;
sway_cmd cmd_ipc_cmd; sway_cmd cmd_ipc_cmd;
sway_cmd cmd_ipc_events; sway_cmd cmd_ipc_events;

View file

@ -165,6 +165,10 @@ struct seat_config {
list_t *attachments; // list of seat_attachment configs list_t *attachments; // list of seat_attachment configs
int hide_cursor_timeout; int hide_cursor_timeout;
enum seat_config_allow_constrain allow_constrain; enum seat_config_allow_constrain allow_constrain;
struct {
char *name;
int size;
} xcursor_theme;
}; };
enum config_dpms { enum config_dpms {

View file

@ -13,6 +13,7 @@ static struct cmd_handler seat_handlers[] = {
{ "fallback", seat_cmd_fallback }, { "fallback", seat_cmd_fallback },
{ "hide_cursor", seat_cmd_hide_cursor }, { "hide_cursor", seat_cmd_hide_cursor },
{ "pointer_constraint", seat_cmd_pointer_constraint }, { "pointer_constraint", seat_cmd_pointer_constraint },
{ "xcursor_theme", seat_cmd_xcursor_theme },
}; };
struct cmd_results *cmd_seat(int argc, char **argv) { struct cmd_results *cmd_seat(int argc, char **argv) {

View file

@ -0,0 +1,33 @@
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include "sway/commands.h"
#include "sway/config.h"
struct cmd_results *seat_cmd_xcursor_theme(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "xcursor_theme", EXPECTED_AT_LEAST, 1)) ||
(error = checkarg(argc, "xcursor_theme", EXPECTED_AT_MOST, 2))) {
return error;
}
if (!config->handler_context.seat_config) {
return cmd_results_new(CMD_FAILURE, "No seat defined");
}
const char *theme_name = argv[0];
unsigned size = 24;
if (argc == 2) {
char *end;
size = strtoul(argv[1], &end, 10);
if (*end) {
return cmd_results_new(
CMD_INVALID, "Expected a positive integer size");
}
}
free(config->handler_context.seat_config->xcursor_theme.name);
config->handler_context.seat_config->xcursor_theme.name = strdup(theme_name);
config->handler_context.seat_config->xcursor_theme.size = size;
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -27,6 +27,8 @@ struct seat_config *new_seat_config(const char* name) {
} }
seat->hide_cursor_timeout = -1; seat->hide_cursor_timeout = -1;
seat->allow_constrain = CONSTRAIN_DEFAULT; seat->allow_constrain = CONSTRAIN_DEFAULT;
seat->xcursor_theme.name = NULL;
seat->xcursor_theme.size = 24;
return seat; return seat;
} }
@ -147,6 +149,12 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
if (source->allow_constrain != CONSTRAIN_DEFAULT) { if (source->allow_constrain != CONSTRAIN_DEFAULT) {
dest->allow_constrain = source->allow_constrain; dest->allow_constrain = source->allow_constrain;
} }
if (source->xcursor_theme.name != NULL) {
free(dest->xcursor_theme.name);
dest->xcursor_theme.name = strdup(source->xcursor_theme.name);
dest->xcursor_theme.size = source->xcursor_theme.size;
}
} }
struct seat_config *copy_seat_config(struct seat_config *seat) { struct seat_config *copy_seat_config(struct seat_config *seat) {
@ -170,6 +178,7 @@ void free_seat_config(struct seat_config *seat) {
seat_attachment_config_free(seat->attachments->items[i]); seat_attachment_config_free(seat->attachments->items[i]);
} }
list_free(seat->attachments); list_free(seat->attachments);
free(seat->xcursor_theme.name);
free(seat); free(seat);
} }

View file

@ -19,6 +19,7 @@
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "sway/layers.h" #include "sway/layers.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
@ -722,17 +723,72 @@ void seat_remove_device(struct sway_seat *seat,
seat_update_capabilities(seat); seat_update_capabilities(seat);
} }
static bool xcursor_manager_is_named(const struct wlr_xcursor_manager *manager,
const char *name) {
return (!manager->name && !name) ||
(name && manager->name && strcmp(name, manager->name) == 0);
}
void seat_configure_xcursor(struct sway_seat *seat) { void seat_configure_xcursor(struct sway_seat *seat) {
// TODO configure theme and size unsigned cursor_size = 24;
const char *cursor_theme = NULL; const char *cursor_theme = NULL;
if (!seat->cursor->xcursor_manager) { const struct seat_config *seat_config = seat_get_config(seat);
seat->cursor->xcursor_manager = if (!seat_config) {
wlr_xcursor_manager_create(cursor_theme, 24); seat_config = seat_get_config_by_name("*");
if (sway_assert(seat->cursor->xcursor_manager,
"Cannot create XCursor manager for theme")) {
return;
} }
if (seat_config) {
cursor_size = seat_config->xcursor_theme.size;
cursor_theme = seat_config->xcursor_theme.name;
}
if (seat == input_manager_get_default_seat()) {
char cursor_size_fmt[16];
snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%d", cursor_size);
setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
if (cursor_theme != NULL) {
setenv("XCURSOR_THEME", cursor_theme, 1);
}
#if HAVE_XWAYLAND
if (!server.xwayland.xcursor_manager ||
!xcursor_manager_is_named(server.xwayland.xcursor_manager,
cursor_theme) ||
server.xwayland.xcursor_manager->size != cursor_size) {
wlr_xcursor_manager_destroy(server.xwayland.xcursor_manager);
server.xwayland.xcursor_manager =
wlr_xcursor_manager_create(cursor_theme, cursor_size);
sway_assert(server.xwayland.xcursor_manager,
"Cannot create XCursor manager for theme");
wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1);
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
server.xwayland.xcursor_manager, "left_ptr", 1);
if (xcursor != NULL) {
struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(
server.xwayland.wlr_xwayland, image->buffer,
image->width * 4, image->width, image->height,
image->hotspot_x, image->hotspot_y);
}
}
#endif
}
/* Create xcursor manager if we don't have one already, or if the
* theme has changed */
if (!seat->cursor->xcursor_manager ||
!xcursor_manager_is_named(
seat->cursor->xcursor_manager, cursor_theme) ||
seat->cursor->xcursor_manager->size != cursor_size) {
wlr_xcursor_manager_destroy(seat->cursor->xcursor_manager);
seat->cursor->xcursor_manager =
wlr_xcursor_manager_create(cursor_theme, cursor_size);
sway_assert(seat->cursor->xcursor_manager,
"Cannot create XCursor manager for theme");
} }
for (int i = 0; i < root->outputs->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {

View file

@ -89,6 +89,7 @@ sway_sources = files(
'commands/seat/fallback.c', 'commands/seat/fallback.c',
'commands/seat/hide_cursor.c', 'commands/seat/hide_cursor.c',
'commands/seat/pointer_constraint.c', 'commands/seat/pointer_constraint.c',
'commands/seat/xcursor_theme.c',
'commands/set.c', 'commands/set.c',
'commands/show_marks.c', 'commands/show_marks.c',
'commands/smart_borders.c', 'commands/smart_borders.c',

View file

@ -168,17 +168,6 @@ void server_fini(struct sway_server *server) {
} }
bool server_start(struct sway_server *server) { bool server_start(struct sway_server *server) {
// TODO: configurable cursor theme and size
int cursor_size = 24;
const char *cursor_theme = NULL;
char cursor_size_fmt[16];
snprintf(cursor_size_fmt, sizeof(cursor_size_fmt), "%d", cursor_size);
setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
if (cursor_theme != NULL) {
setenv("XCURSOR_THEME", cursor_theme, 1);
}
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
if (config->xwayland) { if (config->xwayland) {
sway_log(SWAY_DEBUG, "Initializing Xwayland"); sway_log(SWAY_DEBUG, "Initializing Xwayland");
@ -193,17 +182,7 @@ bool server_start(struct sway_server *server) {
setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true);
server->xwayland.xcursor_manager = /* xcursor configured by the default seat */
wlr_xcursor_manager_create(cursor_theme, cursor_size);
wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1);
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(
server->xwayland.xcursor_manager, "left_ptr", 1);
if (xcursor != NULL) {
struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(server->xwayland.wlr_xwayland, image->buffer,
image->width * 4, image->width, image->height, image->hotspot_x,
image->hotspot_y);
}
} }
#endif #endif

View file

@ -206,6 +206,12 @@ correct seat.
by default) for the seat. This is primarily useful for video games. The by default) for the seat. This is primarily useful for video games. The
"escape" command can be used at runtime to escape from a captured client. "escape" command can be used at runtime to escape from a captured client.
*seat* <name> xcursor_theme <theme> [<size>]
Override the system default XCursor theme. The default seat's
(_seat0_) theme is also used as the default cursor theme in
XWayland, and exported through the _XCURSOR_THEME_ and
_XCURSOR_SIZE_ environment variables.
# SEE ALSO # SEE ALSO
*sway*(5) *sway-output*(5) *xkeyboard-config*(7) *sway*(5) *sway-output*(5) *xkeyboard-config*(7)