From 190546fd315a24c04006fb1b177069933f4350da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 1 Jun 2019 21:05:09 +0200 Subject: [PATCH] add seat sub command 'xcursor_theme' New 'seat xcursor_theme []' 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. --- include/sway/commands.h | 1 + include/sway/config.h | 4 ++ sway/commands/seat.c | 1 + sway/commands/seat/xcursor_theme.c | 33 ++++++++++++++ sway/config/seat.c | 9 ++++ sway/input/seat.c | 70 +++++++++++++++++++++++++++--- sway/meson.build | 1 + sway/server.c | 23 +--------- sway/sway-input.5.scd | 6 +++ 9 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 sway/commands/seat/xcursor_theme.c diff --git a/include/sway/commands.h b/include/sway/commands.h index c4903788..927296b0 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -278,6 +278,7 @@ sway_cmd seat_cmd_cursor; sway_cmd seat_cmd_fallback; sway_cmd seat_cmd_hide_cursor; sway_cmd seat_cmd_pointer_constraint; +sway_cmd seat_cmd_xcursor_theme; sway_cmd cmd_ipc_cmd; sway_cmd cmd_ipc_events; diff --git a/include/sway/config.h b/include/sway/config.h index b94a35f3..311adb16 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -165,6 +165,10 @@ struct seat_config { list_t *attachments; // list of seat_attachment configs int hide_cursor_timeout; enum seat_config_allow_constrain allow_constrain; + struct { + char *name; + int size; + } xcursor_theme; }; enum config_dpms { diff --git a/sway/commands/seat.c b/sway/commands/seat.c index aa36ba95..a827bf74 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -13,6 +13,7 @@ static struct cmd_handler seat_handlers[] = { { "fallback", seat_cmd_fallback }, { "hide_cursor", seat_cmd_hide_cursor }, { "pointer_constraint", seat_cmd_pointer_constraint }, + { "xcursor_theme", seat_cmd_xcursor_theme }, }; struct cmd_results *cmd_seat(int argc, char **argv) { diff --git a/sway/commands/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c new file mode 100644 index 00000000..202f35b9 --- /dev/null +++ b/sway/commands/seat/xcursor_theme.c @@ -0,0 +1,33 @@ +#define _POSIX_C_SOURCE 200809L +#include +#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); +} diff --git a/sway/config/seat.c b/sway/config/seat.c index 04a44e3a..d4190cec 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -27,6 +27,8 @@ struct seat_config *new_seat_config(const char* name) { } seat->hide_cursor_timeout = -1; seat->allow_constrain = CONSTRAIN_DEFAULT; + seat->xcursor_theme.name = NULL; + seat->xcursor_theme.size = 24; return seat; } @@ -147,6 +149,12 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { if (source->allow_constrain != CONSTRAIN_DEFAULT) { 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) { @@ -170,6 +178,7 @@ void free_seat_config(struct seat_config *seat) { seat_attachment_config_free(seat->attachments->items[i]); } list_free(seat->attachments); + free(seat->xcursor_theme.name); free(seat); } diff --git a/sway/input/seat.c b/sway/input/seat.c index ce009d7e..2e386d2c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -19,6 +19,7 @@ #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -722,17 +723,72 @@ void seat_remove_device(struct sway_seat *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) { - // TODO configure theme and size + unsigned cursor_size = 24; const char *cursor_theme = NULL; - if (!seat->cursor->xcursor_manager) { - seat->cursor->xcursor_manager = - wlr_xcursor_manager_create(cursor_theme, 24); - if (sway_assert(seat->cursor->xcursor_manager, - "Cannot create XCursor manager for theme")) { - return; + const struct seat_config *seat_config = seat_get_config(seat); + if (!seat_config) { + seat_config = seat_get_config_by_name("*"); + } + 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) { diff --git a/sway/meson.build b/sway/meson.build index 05cece7a..262d7057 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -89,6 +89,7 @@ sway_sources = files( 'commands/seat/fallback.c', 'commands/seat/hide_cursor.c', 'commands/seat/pointer_constraint.c', + 'commands/seat/xcursor_theme.c', 'commands/set.c', 'commands/show_marks.c', 'commands/smart_borders.c', diff --git a/sway/server.c b/sway/server.c index a403d8b3..b50e3ccc 100644 --- a/sway/server.c +++ b/sway/server.c @@ -168,17 +168,6 @@ void server_fini(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 (config->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); - server->xwayland.xcursor_manager = - 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); - } + /* xcursor configured by the default seat */ } #endif diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index efd3d1af..3191bddf 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -206,6 +206,12 @@ correct seat. 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. +*seat* xcursor_theme [] + 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 *sway*(5) *sway-output*(5) *xkeyboard-config*(7)