diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 59df7737..7a1fa58e 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -4,6 +4,7 @@ packages: - eudev-dev - gdk-pixbuf-dev - json-c-dev + - lcms2-dev - libdisplay-info-dev - libevdev-dev - libinput-dev @@ -38,9 +39,14 @@ tasks: cd sway ninja -C build - build-no-xwayland: | - cd sway + cd wlroots meson configure build -Dxwayland=disabled ninja -C build + sudo ninja -C build install + + cd ../sway + meson configure build --clearcache + ninja -C build - build-static: | cd sway mkdir subprojects diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 9972c01a..e249571e 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -3,6 +3,7 @@ packages: - cairo - gdk-pixbuf2 - json-c + - lcms2 - libdisplay-info - libegl - libinput diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 29c6312a..8084574c 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,6 +8,7 @@ packages: - devel/pkgconf - graphics/cairo - graphics/gdk-pixbuf2 +- graphics/lcms2 - graphics/wayland - graphics/wayland-protocols - textproc/scdoc diff --git a/common/pango.c b/common/pango.c index 288569b3..e52b52b9 100644 --- a/common/pango.c +++ b/common/pango.c @@ -53,6 +53,8 @@ size_t escape_markup_text(const char *src, char *dest) { PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, const char *text, double scale, bool markup) { PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); + PangoAttrList *attrs; if (markup) { char *buf; @@ -104,6 +106,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { cairo_t *cairo = cairo_create(NULL); PangoContext *pango = pango_cairo_create_context(cairo); + pango_context_set_round_glyph_positions(pango, false); // When passing NULL as a language, pango uses the current locale. PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); diff --git a/include/sway/commands.h b/include/sway/commands.h index ff44d54c..aebb5e1d 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -250,6 +250,7 @@ sway_cmd input_cmd_seat; sway_cmd input_cmd_accel_profile; sway_cmd input_cmd_calibration_matrix; sway_cmd input_cmd_click_method; +sway_cmd input_cmd_clickfinger_button_map; sway_cmd input_cmd_drag; sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_dwt; @@ -284,6 +285,7 @@ sway_cmd input_cmd_xkb_variant; sway_cmd output_cmd_adaptive_sync; sway_cmd output_cmd_background; +sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; sway_cmd output_cmd_dpms; sway_cmd output_cmd_enable; diff --git a/include/sway/config.h b/include/sway/config.h index 33929e90..94587f0b 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "../include/config.h" @@ -148,6 +149,7 @@ struct input_config { int accel_profile; struct calibration_matrix calibration_matrix; int click_method; + int clickfinger_button_map; int drag; int drag_lock; int dwt; @@ -285,6 +287,8 @@ struct output_config { int max_render_time; // In milliseconds int adaptive_sync; enum render_bit_depth render_bit_depth; + bool set_color_transform; + struct wlr_color_transform *color_transform; char *background; char *background_option; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 8da345ea..ae546821 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -7,6 +7,10 @@ #include "list.h" #include "tree/view.h" +#if WLR_HAS_XWAYLAND +#include "sway/xwayland.h" +#endif + enum criteria_type { CT_COMMAND = 1 << 0, CT_ASSIGN_OUTPUT = 1 << 1, @@ -36,7 +40,7 @@ struct criteria { struct pattern *app_id; struct pattern *con_mark; uint32_t con_id; // internal ID -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct pattern *class; uint32_t id; // X11 window ID struct pattern *instance; diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 45c75199..b014e18f 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -5,10 +5,11 @@ #include #include #include -#include "sway/server.h" #include "sway/config.h" #include "list.h" +struct sway_server; + struct sway_input_device { char *identifier; struct wlr_input_device *wlr_device; diff --git a/include/sway/output.h b/include/sway/output.h index d546d488..2189c6e8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -66,6 +66,8 @@ struct sway_output { struct wl_signal disable; } events; + struct wlr_color_transform *color_transform; + struct timespec last_presentation; uint32_t refresh_nsec; int max_render_time; // In milliseconds diff --git a/include/sway/server.h b/include/sway/server.h index c71851f6..abf1b6b4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -5,7 +5,7 @@ #include "config.h" #include "list.h" #include "sway/desktop/idle_inhibit_v1.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -59,7 +59,7 @@ struct sway_server { struct wlr_tablet_manager_v2 *tablet_v2; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; @@ -81,6 +81,8 @@ struct sway_server { struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener pointer_constraint; + struct wlr_xdg_output_manager_v1 *xdg_output_manager_v1; + struct wlr_output_manager_v1 *output_manager_v1; struct wl_listener output_manager_apply; struct wl_listener output_manager_test; @@ -133,6 +135,8 @@ struct sway_server { // Stores the nodes that have been marked as "dirty" and will be put into // the pending transaction. list_t *dirty_nodes; + + struct wl_event_source *delayed_modeset; }; extern struct sway_server server; @@ -165,7 +169,7 @@ void sway_session_lock_add_output(struct sway_session_lock *lock, bool sway_session_lock_has_surface(struct sway_session_lock *lock, struct wlr_surface *surface); void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); #endif void handle_server_decoration(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 93f6bfbb..8bf1955d 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -175,8 +175,6 @@ struct sway_container *container_obstructing_fullscreen_container(struct sway_co bool container_has_ancestor(struct sway_container *container, struct sway_container *ancestor); -void container_update_textures_recursive(struct sway_container *con); - void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 15df0f55..7de0abcd 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -2,12 +2,12 @@ #define _SWAY_ROOT_H #include #include +#include #include #include #include #include "sway/tree/container.h" #include "sway/tree/node.h" -#include "config.h" #include "list.h" extern struct sway_root *root; @@ -47,7 +47,7 @@ struct sway_root { struct wlr_scene_tree *shell_top; struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_scene_tree *unmanaged; #endif struct wlr_scene_tree *shell_overlay; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7faacdcc..3ae8cf22 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -1,10 +1,11 @@ #ifndef _SWAY_VIEW_H #define _SWAY_VIEW_H #include +#include #include #include #include "sway/config.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #endif #include "sway/input/input-manager.h" @@ -15,7 +16,7 @@ struct sway_xdg_decoration; enum sway_view_type { SWAY_VIEW_XDG_SHELL, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND SWAY_VIEW_XWAYLAND, #endif }; @@ -27,7 +28,7 @@ enum sway_view_prop { VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, VIEW_PROP_WINDOW_ROLE, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND VIEW_PROP_X11_WINDOW_ID, VIEW_PROP_X11_PARENT_ID, #endif @@ -98,7 +99,7 @@ struct sway_view { union { struct wlr_xdg_toplevel *wlr_xdg_toplevel; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; #endif }; @@ -127,7 +128,7 @@ struct sway_xdg_shell_view { struct wl_listener unmap; struct wl_listener destroy; }; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_xwayland_view { struct sway_view view; @@ -293,7 +294,7 @@ void view_center_and_clip_surface(struct sway_view *view); struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface); #endif diff --git a/meson.build b/meson.build index 1043e4ba..c602d008 100644 --- a/meson.build +++ b/meson.build @@ -45,7 +45,7 @@ subproject( required: false, version: wlroots_version, ) -wlroots = dependency('wlroots', version: wlroots_version) +wlroots = dependency('wlroots-0.18', version: wlroots_version, fallback: 'wlroots') wlroots_features = { 'xwayland': false, 'libinput_backend': false, @@ -57,10 +57,6 @@ foreach name, _ : wlroots_features wlroots_features += { name: have } endforeach -if get_option('xwayland').enabled() and not wlroots_features['xwayland'] - error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') -endif - null_dep = dependency('', required: false) jsonc = dependency('json-c', version: '>=0.13') @@ -68,7 +64,7 @@ pcre2 = dependency('libpcre2-8') wayland_server = dependency('wayland-server', version: '>=1.21.0') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -wayland_protos = dependency('wayland-protocols', version: '>=1.24') +wayland_protos = dependency('wayland-protocols', version: '>=1.24', default_options: ['tests=false']) xkbcommon = dependency('xkbcommon', version: '>=1.5.0') cairo = dependency('cairo') pango = dependency('pango') @@ -76,17 +72,15 @@ pangocairo = dependency('pangocairo') gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) pixman = dependency('pixman-1') libevdev = dependency('libevdev') -libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep -xcb = dependency('xcb', required: get_option('xwayland')) +libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep +xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep drm = dependency('libdrm') libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep math = cc.find_library('m') rt = cc.find_library('rt') -xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) +xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep threads = dependency('threads') # for pthread_setschedparam -have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] - if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') @@ -110,7 +104,6 @@ have_tray = (not get_option('tray').disabled()) and tray_deps_found conf_data = configuration_data() -conf_data.set10('HAVE_XWAYLAND', have_xwayland) conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found()) conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd') conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') @@ -271,7 +264,6 @@ endif subdir('completions') summary({ - 'xwayland': have_xwayland, 'gdk-pixbuf': gdk_pixbuf.found(), 'tray': have_tray, 'man-pages': scdoc.found(), diff --git a/meson_options.txt b/meson_options.txt index 8d0d6509..506ecc9a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,7 +4,6 @@ option('bash-completions', type: 'boolean', value: true, description: 'Install b option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar') option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') -option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/protocols/meson.build b/protocols/meson.build index 81edb584..6eac8542 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -7,10 +7,10 @@ wayland_scanner = find_program( ) protocols = [ + wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', diff --git a/sway/commands/input.c b/sway/commands/input.c index 306c40f7..35846b1c 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -11,6 +11,7 @@ static const struct cmd_handler input_handlers[] = { { "accel_profile", input_cmd_accel_profile }, { "calibration_matrix", input_cmd_calibration_matrix }, { "click_method", input_cmd_click_method }, + { "clickfinger_button_map", input_cmd_clickfinger_button_map }, { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, diff --git a/sway/commands/input/clickfinger_button_map.c b/sway/commands/input/clickfinger_button_map.c new file mode 100644 index 00000000..57d6e39a --- /dev/null +++ b/sway/commands/input/clickfinger_button_map.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_clickfinger_button_map(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "clickfinger_button_map", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, "No input device defined."); + } + + if (strcasecmp(argv[0], "lrm") == 0) { + ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM; + } else if (strcasecmp(argv[0], "lmr") == 0) { + ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'clickfinger_button_map '"); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 08d99bf0..3cea026e 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -5,6 +5,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "log.h" #if WLR_HAS_LIBINPUT_BACKEND diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index ecac8e6c..8d600fca 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "log.h" struct xkb_switch_layout_action { diff --git a/sway/commands/move.c b/sway/commands/move.c index 8addf26e..ff656cfb 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -11,6 +11,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" diff --git a/sway/commands/output.c b/sway/commands/output.c index 5e5d31b3..b822e770 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -10,6 +10,7 @@ static const struct cmd_handler output_handlers[] = { { "adaptive_sync", output_cmd_adaptive_sync }, { "background", output_cmd_background }, { "bg", output_cmd_background }, + { "color_profile", output_cmd_color_profile }, { "disable", output_cmd_disable }, { "dpms", output_cmd_dpms }, { "enable", output_cmd_enable }, diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c new file mode 100644 index 00000000..792bd55f --- /dev/null +++ b/sway/commands/output/color_profile.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" + +static bool read_file_into_buf(const char *path, void **buf, size_t *size) { + /* Why not use fopen/fread directly? glibc will succesfully open directories, + * not just files, and supports seeking on them. Instead, we directly + * work with file descriptors and use the more consistent open/fstat/read. */ + int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC); + if (fd == -1) { + return false; + } + char *b = NULL; + struct stat info; + if (fstat(fd, &info) == -1) { + goto fail; + } + // only regular files, to avoid issues with e.g. opening pipes + if (!S_ISREG(info.st_mode)) { + goto fail; + } + off_t s = info.st_size; + if (s <= 0) { + goto fail; + } + b = calloc(1, s); + if (!b) { + goto fail; + } + size_t nread = 0; + while (nread < (size_t)s) { + size_t to_read = (size_t)s - nread; + ssize_t r = read(fd, b + nread, to_read); + if ((r == -1 && errno != EINTR) || r == 0) { + goto fail; + } + nread += (size_t)r; + } + close(fd); + *buf = b; + *size = (size_t)s; + return true; // success +fail: + free(b); + close(fd); + return false; +} + +struct cmd_results *output_cmd_color_profile(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); + } + + if (strcmp(*argv, "srgb") == 0) { + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = NULL; + config->handler_context.output_config->set_color_transform = true; + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + } else if (strcmp(*argv, "icc") == 0) { + if (argc < 2) { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: icc type requires a file"); + } + void *data = NULL; + size_t size = 0; + if (!read_file_into_buf(argv[1], &data, &size)) { + return cmd_results_new(CMD_FAILURE, + "Failed to load color profile: could not read ICC file"); + } + + struct wlr_color_transform *tmp = + wlr_color_transform_init_linear_to_icc(data, size); + if (!tmp) { + free(data); + return cmd_results_new(CMD_FAILURE, + "Failed to load color profile: failed to initialize transform from ICC"); + } + free(data); + + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = tmp; + config->handler_context.output_config->set_color_transform = true; + + config->handler_context.leftovers.argc = argc - 2; + config->handler_context.leftovers.argv = argv + 2; + } else { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: first argument should be icc|srgb"); + } + + return NULL; +} diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index df7c379d..434e6bbb 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -5,6 +5,7 @@ #include #include "sway/commands.h" #include "sway/input/cursor.h" +#include "sway/server.h" static struct cmd_results *press_or_release(struct sway_cursor *cursor, char *action, char *button_str); diff --git a/sway/commands/seat/pointer_constraint.c b/sway/commands/seat/pointer_constraint.c index 3890ebde..38f85bcd 100644 --- a/sway/commands/seat/pointer_constraint.c +++ b/sway/commands/seat/pointer_constraint.c @@ -4,6 +4,7 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" +#include "sway/server.h" enum operation { OP_ENABLE, diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c index 7c7f99cf..df68618d 100644 --- a/sway/commands/seat/shortcuts_inhibitor.c +++ b/sway/commands/seat/shortcuts_inhibitor.c @@ -2,6 +2,7 @@ #include "sway/commands.h" #include "sway/input/seat.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "util.h" static struct cmd_results *handle_action(struct seat_config *sc, diff --git a/sway/commands/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c index ffa1a5c9..2dfd1b9f 100644 --- a/sway/commands/shortcuts_inhibitor.c +++ b/sway/commands/shortcuts_inhibitor.c @@ -3,6 +3,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/input/seat.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/view.h" diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e142eede..c0b0d0b9 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -18,7 +18,7 @@ static bool test_con_id(struct sway_container *container, void *data) { return container->node.id == *con_id; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static bool test_id(struct sway_container *container, void *data) { xcb_window_t *wid = data; return (container->view && container->view->type == SWAY_VIEW_XWAYLAND @@ -53,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = root_find_container(test_id, &id); #endif diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index 584a8e3a..c0b175fc 100644 --- a/sway/commands/xwayland.c +++ b/sway/commands/xwayland.c @@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { return error; } -#ifdef HAVE_XWAYLAND +#ifdef WLR_HAS_XWAYLAND enum xwayland_mode xwayland; if (strcmp(argv[0], "force") == 0) { xwayland = XWAYLAND_MODE_IMMEDIATE; diff --git a/sway/config.c b/sway/config.c index b1475cf5..2a3ee1f1 100644 --- a/sway/config.c +++ b/sway/config.c @@ -23,6 +23,7 @@ #include "sway/config.h" #include "sway/criteria.h" #include "sway/desktop/transaction.h" +#include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/arrange.h" #include "sway/tree/root.h" diff --git a/sway/config/bar.c b/sway/config/bar.c index 908b2865..ecefb61a 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -12,6 +12,7 @@ #include "sway/config.h" #include "sway/input/keyboard.h" #include "sway/output.h" +#include "sway/server.h" #include "config.h" #include "list.h" #include "log.h" diff --git a/sway/config/input.c b/sway/config/input.c index de3b21ed..e5694eff 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -3,6 +3,7 @@ #include #include "sway/config.h" #include "sway/input/keyboard.h" +#include "sway/server.h" #include "log.h" struct input_config *new_input_config(const char* identifier) { @@ -27,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) { input->dwtp = INT_MIN; input->send_events = INT_MIN; input->click_method = INT_MIN; + input->clickfinger_button_map = INT_MIN; input->middle_emulation = INT_MIN; input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; @@ -54,6 +56,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->click_method != INT_MIN) { dst->click_method = src->click_method; } + if (src->clickfinger_button_map != INT_MIN) { + dst->clickfinger_button_map = src->clickfinger_button_map; + } if (src->drag != INT_MIN) { dst->drag = src->drag; } diff --git a/sway/config/output.c b/sway/config/output.c index fb1956df..e64efb7f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -13,6 +13,7 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/root.h" #include "log.h" #include "util.h" @@ -75,6 +76,8 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + oc->set_color_transform = false; + oc->color_transform = NULL; oc->power = -1; return oc; } @@ -190,6 +193,14 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = src->render_bit_depth; } + if (src->set_color_transform) { + if (src->color_transform) { + wlr_color_transform_ref(src->color_transform); + } + wlr_color_transform_unref(dst->color_transform); + dst->set_color_transform = true; + dst->color_transform = src->color_transform; + } if (src->background) { free(dst->background); dst->background = strdup(src->background); @@ -448,7 +459,7 @@ static void queue_output_config(struct output_config *oc, #endif } if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); + sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); wlr_output_state_set_transform(pending, tr); } @@ -556,6 +567,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output->max_render_time = oc->max_render_time; } + if (oc && oc->set_color_transform) { + if (oc->color_transform) { + wlr_color_transform_ref(oc->color_transform); + } + wlr_color_transform_unref(output->color_transform); + output->color_transform = oc->color_transform; + } return true; } @@ -703,21 +721,18 @@ static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; struct wlr_output_state *state = &backend_state->base; + if (!backend_state->output->adaptive_sync_supported) { + return search_finish(ctx, output_idx); + } + if (cfg->config && cfg->config->adaptive_sync == 1) { wlr_output_state_set_adaptive_sync_enabled(state, true); if (search_finish(ctx, output_idx)) { return true; } } - if (!cfg->config || cfg->config->adaptive_sync != -1) { - wlr_output_state_set_adaptive_sync_enabled(state, false); - if (search_finish(ctx, output_idx)) { - return true; - } - } - // If adaptive sync has not been set, or fallback in case we are on a - // backend that cannot disable adaptive sync such as the wayland backend. - state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; + + wlr_output_state_set_adaptive_sync_enabled(state, false); return search_finish(ctx, output_idx); } @@ -909,6 +924,7 @@ bool apply_output_configs(struct matched_output_config *configs, struct wlr_scene_output_state_options opts = { .swapchain = wlr_output_swapchain_manager_get_swapchain( &swapchain_mgr, backend_state->output), + .color_transform = cfg->output->color_transform, }; struct wlr_scene_output *scene_output = cfg->output->scene_output; struct wlr_output_state *state = &backend_state->base; @@ -996,6 +1012,7 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); + wlr_color_transform_unref(oc->color_transform); free(oc); } diff --git a/sway/criteria.c b/sway/criteria.c index e16b4fa8..2b7290c0 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -7,6 +7,7 @@ #include "sway/criteria.h" #include "sway/tree/container.h" #include "sway/config.h" +#include "sway/server.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -22,7 +23,7 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->app_id && !criteria->con_mark && !criteria->con_id -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND && !criteria->class && !criteria->id && !criteria->instance @@ -90,7 +91,7 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->title); pattern_destroy(criteria->shell); pattern_destroy(criteria->app_id); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND pattern_destroy(criteria->class); pattern_destroy(criteria->instance); pattern_destroy(criteria->window_role); @@ -110,7 +111,7 @@ static int regex_cmp(const char *item, const pcre2_code *regex) { return result; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static bool view_has_window_type(struct sway_view *view, enum atom_name name) { if (view->type != SWAY_VIEW_XWAYLAND) { return false; @@ -251,7 +252,7 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { @@ -428,7 +429,7 @@ list_t *criteria_get_containers(struct criteria *criteria) { return matches; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static enum atom_name parse_window_type(const char *type) { if (strcasecmp(type, "normal") == 0) { return NET_WM_WINDOW_TYPE_NORMAL; @@ -461,7 +462,7 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND T_CLASS, T_ID, T_INSTANCE, @@ -487,7 +488,7 @@ static enum criteria_token token_from_name(char *name) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND } else if (strcmp(name, "class") == 0) { return T_CLASS; } else if (strcmp(name, "id") == 0) { @@ -566,7 +567,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_CON_MARK: pattern_create(&criteria->con_mark, value); break; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case T_CLASS: pattern_create(&criteria->class, value); break; @@ -674,7 +675,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; struct criteria *criteria = calloc(1, sizeof(struct criteria)); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND criteria->window_type = ATOM_LAST; // default value #endif char *name = NULL, *value = NULL; diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 28043d19..2362e1ba 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -4,6 +4,7 @@ #include "sway/input/seat.h" #include "sway/output.h" #include "sway/desktop/launcher.h" +#include "sway/server.h" #include "sway/tree/node.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 2722e556..f936b2a8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -243,13 +243,17 @@ static int output_repaint_timer_handler(void *data) { output_configure_scene(output, &root->root_scene->tree.node, 1.0f); - if (output->gamma_lut_changed) { - struct wlr_output_state pending; - wlr_output_state_init(&pending); - if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { - return 0; - } + struct wlr_scene_output_state_options opts = { + .color_transform = output->color_transform, + }; + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { + return 0; + } + + if (output->gamma_lut_changed) { output->gamma_lut_changed = false; struct wlr_gamma_control_v1 *gamma_control = wlr_gamma_control_manager_v1_get_control( @@ -259,17 +263,16 @@ static int output_repaint_timer_handler(void *data) { return 0; } - if (!wlr_output_commit_state(output->wlr_output, &pending)) { + if (!wlr_output_test_state(output->wlr_output, &pending)) { wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - wlr_output_state_finish(&pending); - return 0; + wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } - - wlr_output_state_finish(&pending); - return 0; } - wlr_scene_output_commit(output->scene_output, NULL); + if (!wlr_output_commit_state(output->wlr_output, &pending)) { + sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); + } + wlr_output_state_finish(&pending); return 0; } @@ -362,6 +365,26 @@ static void update_output_manager_config(struct sway_server *server) { ipc_event_output(); } +static int timer_modeset_handle(void *data) { + struct sway_server *server = data; + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + + apply_all_output_configs(); + transaction_commit_dirty(); + update_output_manager_config(server); + + return 0; +} + +static void request_modeset(struct sway_server *server) { + if (server->delayed_modeset == NULL) { + server->delayed_modeset = wl_event_loop_add_timer(server->wl_event_loop, + timer_modeset_handle, server); + wl_event_source_timer_update(server->delayed_modeset, 10); + } +} + static void begin_destroy(struct sway_output *output) { struct sway_server *server = output->server; @@ -385,9 +408,7 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; - transaction_commit_dirty(); - - update_output_manager_config(server); + request_modeset(server); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -521,11 +542,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - apply_all_output_configs(); - - transaction_commit_dirty(); - - update_output_manager_config(server); + request_modeset(server); } void handle_output_layout_change(struct wl_listener *listener, @@ -677,5 +694,5 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, break; } store_output_config(oc); - apply_all_output_configs(); + request_modeset(output->server); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 042141ab..7568990b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -10,6 +10,7 @@ #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -313,7 +314,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children if (activated) { arrange_container(child, width, height - title_bar_height, - false, 0); + title_bar_height == 0, 0); } else { disable_container(child); } @@ -342,7 +343,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children if (activated) { arrange_container(child, width, height - title_height, - false, 0); + title_bar_height == 0, 0); } else { disable_container(child); } @@ -761,7 +762,7 @@ static bool should_configure(struct sway_node *node, } struct sway_container_state *cstate = &node->sway_container->current; struct sway_container_state *istate = &instruction->container_state; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Xwayland views are position-aware and need to be reconfigured // when their position changes. if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 7c417891..fdfa7b65 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -289,6 +289,8 @@ static void handle_commit(struct wl_listener *listener, void *data) { } // XXX: https://github.com/swaywm/sway/issues/2176 wlr_xdg_surface_schedule_configure(xdg_surface); + wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel, + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); // TODO: wlr_xdg_toplevel_set_bounds() return; } @@ -575,7 +577,4 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); xdg_toplevel->base->data = xdg_shell_view; - - wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, - XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3d04826c..235951d4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -25,6 +25,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/scene_descriptor.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" @@ -107,7 +108,7 @@ struct sway_node *node_at_coords( return NULL; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { return NULL; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index f74d0658..efb9ac39 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -13,6 +13,7 @@ #include "sway/input/seat.h" #include "sway/input/cursor.h" #include "sway/ipc-server.h" +#include "sway/server.h" #include "log.h" #if WLR_HAS_SESSION @@ -508,12 +509,13 @@ static void handle_key_event(struct sway_keyboard *keyboard, } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { - // If the pressed event was sent to a client, also send the released + // If the pressed event was sent to a client and we have a focused + // surface immediately before this event, also send the released // event. In particular, don't send the released event to the IM grab. bool pressed_sent = update_shortcut_state( &keyboard->state_pressed_sent, event->keycode, event->state, keyinfo.keycode, 0); - if (pressed_sent) { + if (pressed_sent && seat->wlr_seat->keyboard_state.focused_surface) { wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); @@ -959,16 +961,8 @@ cleanup: free(sway_group); } -void sway_keyboard_configure(struct sway_keyboard *keyboard) { - struct input_config *input_config = - input_device_get_config(keyboard->seat_device->input_device); - - if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), - "sway_keyboard_configure should not be called with a " - "keyboard group's keyboard")) { - return; - } - +static void sway_keyboard_set_layout(struct sway_keyboard *keyboard, + struct input_config *input_config) { struct xkb_keymap *keymap = sway_keyboard_compile_keymap(input_config, NULL); if (!keymap) { sway_log(SWAY_ERROR, "Failed to compile keymap. Attempting defaults"); @@ -984,31 +978,13 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { !wlr_keyboard_keymaps_match(keyboard->keymap, keymap) : true; bool effective_layout_changed = keyboard->effective_layout != 0; - int repeat_rate = 25; - if (input_config && input_config->repeat_rate != INT_MIN) { - repeat_rate = input_config->repeat_rate; - } - int repeat_delay = 600; - if (input_config && input_config->repeat_delay != INT_MIN) { - repeat_delay = input_config->repeat_delay; - } - - bool repeat_info_changed = keyboard->repeat_rate != repeat_rate || - keyboard->repeat_delay != repeat_delay; - - if (keymap_changed || repeat_info_changed || config->reloading) { + if (keymap_changed || config->reloading) { xkb_keymap_unref(keyboard->keymap); keyboard->keymap = keymap; keyboard->effective_layout = 0; - keyboard->repeat_rate = repeat_rate; - keyboard->repeat_delay = repeat_delay; sway_keyboard_group_remove_invalid(keyboard); - wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); - wlr_keyboard_set_repeat_info(keyboard->wlr, - keyboard->repeat_rate, keyboard->repeat_delay); - if (!keyboard->wlr->group) { sway_keyboard_group_add(keyboard); } @@ -1059,6 +1035,49 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_seat_set_keyboard(seat, keyboard->wlr); } + if (keymap_changed) { + ipc_event_input("xkb_keymap", + keyboard->seat_device->input_device); + } else if (effective_layout_changed) { + ipc_event_input("xkb_layout", + keyboard->seat_device->input_device); + } +} + +void sway_keyboard_configure(struct sway_keyboard *keyboard) { + struct input_config *input_config = + input_device_get_config(keyboard->seat_device->input_device); + + if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), + "sway_keyboard_configure should not be called with a " + "keyboard group's keyboard")) { + return; + } + + int repeat_rate = 25; + if (input_config && input_config->repeat_rate != INT_MIN) { + repeat_rate = input_config->repeat_rate; + } + int repeat_delay = 600; + if (input_config && input_config->repeat_delay != INT_MIN) { + repeat_delay = input_config->repeat_delay; + } + + bool repeat_info_changed = keyboard->repeat_rate != repeat_rate || + keyboard->repeat_delay != repeat_delay; + + if (repeat_info_changed || config->reloading) { + keyboard->repeat_rate = repeat_rate; + keyboard->repeat_delay = repeat_delay; + + wlr_keyboard_set_repeat_info(keyboard->wlr, + keyboard->repeat_rate, keyboard->repeat_delay); + } + + if (!keyboard->seat_device->input_device->is_virtual) { + sway_keyboard_set_layout(keyboard, input_config); + } + wl_list_remove(&keyboard->keyboard_key.link); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; @@ -1068,13 +1087,6 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; - if (keymap_changed) { - ipc_event_input("xkb_keymap", - keyboard->seat_device->input_device); - } else if (effective_layout_changed) { - ipc_event_input("xkb_layout", - keyboard->seat_device->input_device); - } } void sway_keyboard_destroy(struct sway_keyboard *keyboard) { diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 0266c7a9..2fec290e 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -132,6 +132,16 @@ static bool set_click_method(struct libinput_device *device, return true; } +static bool set_clickfinger_button_map(struct libinput_device *device, + enum libinput_config_clickfinger_button_map map) { + if (libinput_device_config_click_get_clickfinger_button_map(device) == map) { + return false; + } + sway_log(SWAY_DEBUG, "clickfinger_set_button_map(%d)", map); + log_status(libinput_device_config_click_set_clickfinger_button_map(device, map)); + return true; +} + static bool set_middle_emulation(struct libinput_device *dev, enum libinput_config_middle_emulation_state mid) { if (!libinput_device_config_middle_emulation_is_available(dev) || @@ -281,6 +291,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->click_method != INT_MIN) { changed |= set_click_method(device, ic->click_method); } + if (ic->clickfinger_button_map != INT_MIN) { + changed |= set_clickfinger_button_map(device, ic->clickfinger_button_map); + } if (ic->middle_emulation != INT_MIN) { changed |= set_middle_emulation(device, ic->middle_emulation); } @@ -356,6 +369,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_left_handed_get_default(device)); changed |= set_click_method(device, libinput_device_config_click_get_default_method(device)); + changed |= set_clickfinger_button_map(device, + libinput_device_config_click_get_default_clickfinger_button_map(device)); changed |= set_middle_emulation(device, libinput_device_config_middle_emulation_get_default_enabled(device)); changed |= set_scroll_method(device, diff --git a/sway/input/seat.c b/sway/input/seat.c index 0c5672bc..9a00a3e2 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -190,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { node->sway_container->view : NULL; if (view && seat_is_input_allowed(seat, view->surface)) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -802,11 +802,10 @@ static void seat_configure_keyboard(struct sway_seat *seat, return; } - // force notify reenter to pick up the new configuration. This reuses + // Notify reenter to pick up the new configuration. This reuses // the current focused surface to avoid breaking input grabs. struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; if (surface) { - wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); seat_keyboard_notify_enter(seat, surface); } } @@ -1002,7 +1001,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { setenv("XCURSOR_THEME", cursor_theme, 1); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || !xcursor_manager_is_named(server.xwayland.xcursor_manager, cursor_theme) || diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 0c6f7c5e..f4a0f463 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -11,11 +11,12 @@ #include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/scene_descriptor.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -234,7 +235,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, node->sway_container : NULL; struct wlr_layer_surface_v1 *layer; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; #endif if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && @@ -268,7 +269,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, seat_set_focus_container(seat, cont); seatop_begin_down(seat, node->sway_container, sx, sy); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Handle tapping on an xwayland unmanaged view else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && @@ -514,7 +515,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, return; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Handle clicking on xwayland unmanaged view struct wlr_xwayland_surface *xsurface; if (surface && diff --git a/sway/input/switch.c b/sway/input/switch.c index 831f4dbf..6aab4ad0 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -1,5 +1,6 @@ #include "sway/config.h" #include "sway/input/switch.h" +#include "sway/server.h" #include "log.h" struct sway_switch *sway_switch_create(struct sway_seat *seat, diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 2863642a..ec1e4f68 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -7,6 +7,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/input/tablet.h" +#include "sway/server.h" #if WLR_HAS_LIBINPUT_BACKEND #include diff --git a/sway/input/text_input.c b/sway/input/text_input.c index c38a3bb2..580a9f54 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -9,6 +9,8 @@ #include "sway/input/text_input.h" #include "sway/input/text_input_popup.h" #include "sway/layers.h" +#include "sway/server.h" + static void input_popup_update(struct sway_input_popup *popup); static struct sway_text_input *relay_get_focusable_text_input( @@ -66,11 +68,13 @@ static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void * struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_keyboard_grab_destroy); struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat; wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); if (keyboard_grab->keyboard) { // send modifier state to original client - wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, + wlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard); + wlr_seat_keyboard_notify_modifiers(wlr_seat, &keyboard_grab->keyboard->modifiers); } } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 81ca3483..e512a223 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -11,6 +11,7 @@ #include "log.h" #include "sway/config.h" #include "sway/ipc-json.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -154,7 +155,7 @@ static json_object *ipc_json_output_mode_description( return mode_object; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static const char *ipc_json_xwindow_type_description(struct sway_view *view) { struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; struct sway_xwayland *xwayland = &server.xwayland; @@ -577,9 +578,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object bool visible = view_is_visible(c->view); json_object_object_add(object, "visible", json_object_new_boolean(visible)); + bool has_titlebar = c->title_bar.tree->node.enabled; struct wlr_box window_box = { c->pending.content_x - c->pending.x, - (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, + has_titlebar ? 0 : c->pending.content_y - c->pending.y, c->pending.content_width, c->pending.content_height }; @@ -633,7 +635,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_new_string(ipc_json_content_type_description(content_type))); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", json_object_new_int(view_get_x11_window_id(c->view))); @@ -990,6 +992,18 @@ static json_object *describe_libinput_device(struct libinput_device *device) { } json_object_object_add(object, "click_method", json_object_new_string(click_method)); + + const char *button_map = "unknown"; + switch (libinput_device_config_click_get_clickfinger_button_map(device)) { + case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM: + button_map = "lrm"; + break; + case LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR: + button_map = "lmr"; + break; + } + json_object_object_add(object, "clickfinger_button_map", + json_object_new_string(button_map)); } if (libinput_device_config_middle_emulation_is_available(device)) { @@ -1107,9 +1121,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { struct xkb_keymap *keymap = keyboard->keymap; struct xkb_state *state = keyboard->xkb_state; - json_object_object_add(object, "repeat_delay", + json_object_object_add(object, "repeat_delay", json_object_new_int(keyboard->repeat_info.delay)); - json_object_object_add(object, "repeat_rate", + json_object_object_add(object, "repeat_rate", json_object_new_int(keyboard->repeat_info.rate)); json_object *layouts_arr = json_object_new_array(); diff --git a/sway/meson.build b/sway/meson.build index bfc8e110..efd74cba 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -155,6 +155,7 @@ sway_sources = files( 'commands/input/accel_profile.c', 'commands/input/calibration_matrix.c', 'commands/input/click_method.c', + 'commands/input/clickfinger_button_map.c', 'commands/input/drag.c', 'commands/input/drag_lock.c', 'commands/input/dwt.c', @@ -203,6 +204,7 @@ sway_sources = files( 'commands/output/toggle.c', 'commands/output/transform.c', 'commands/output/unplug.c', + 'commands/output/color_profile.c', 'tree/arrange.c', 'tree/container.c', @@ -232,7 +234,7 @@ sway_deps = [ xcb_icccm, ] -if have_xwayland +if wlroots_features['xwayland'] sway_sources += 'desktop/xwayland.c' endif diff --git a/sway/server.c b/sway/server.c index 180d3a6b..edbc1a4b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -56,7 +56,7 @@ #include "sway/input/cursor.h" #include "sway/tree/root.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #include "sway/xwayland.h" #endif @@ -113,12 +113,13 @@ static bool is_privileged(const struct wl_global *global) { global == server.input->keyboard_shortcuts_inhibit->global || global == server.input->virtual_keyboard->global || global == server.input->virtual_pointer->global || - global == server.input->transient_seat_manager->global; + global == server.input->transient_seat_manager->global || + global == server.xdg_output_manager_v1->global; } static bool filter_global(const struct wl_client *client, const struct wl_global *global, void *data) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; if (xwayland && global == xwayland->shell_v1->global) { return xwayland->server != NULL && client == xwayland->server->client; @@ -275,7 +276,8 @@ bool server_init(struct sway_server *server) { wl_signal_add(&root->output_layout->events.change, &server->output_layout_change); - wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); + server->xdg_output_manager_v1 = + wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); sway_idle_inhibit_manager_v1_init(); @@ -437,7 +439,7 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); @@ -447,7 +449,7 @@ void server_fini(struct sway_server *server) { } bool server_start(struct sway_server *server) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (config->xwayland != XWAYLAND_MODE_DISABLED) { sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", config->xwayland == XWAYLAND_MODE_LAZY); diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 442311bb..fbef2a32 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -143,6 +143,12 @@ The following commands may only be used in the configuration file. *input* click_method none|button_areas|clickfinger Changes the click method for the specified device. +*input* clickfinger_button_map lrm|lmr + Specifies which button mapping to use for clickfinger. _lrm_ treats 1 finger as + left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ + treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as + right click. + *input* drag enabled|disabled Enables or disables tap-and-drag for specified input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 2f697248..e90abcbb 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1168,7 +1168,7 @@ following properties will be included for devices that support them: : Whether tap to click is enabled. It can be _enabled_ or _disabled_ |- tap_button_map : string -: The finger to button mapping in use. It can be _lmr_ or _lrm_ +: The finger to button mapping in use for tapping. It can be _lmr_ or _lrm_ |- tap_drag : string : Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_ @@ -1190,6 +1190,9 @@ following properties will be included for devices that support them: |- click_method : string : The click method in use. It can be _none_, _button_areas_, or _clickfinger_ +|- click_button_map +: string +: The finger to button mapping in use for clickfinger. It can be _lmr_ or _lrm_ |- middle_emulation : string : Whether middle emulation is enabled. It can be _enabled_ or _disabled_ diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 7d088d5d..6d7c0860 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,6 +178,18 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. +*output* color_profile srgb|[icc ] + Sets the color profile for an output. The default is _srgb_. should be a + path to a display ICC profile. + + Not all renderers support this feature; currently it only works with the + the Vulkan renderer. Even where supported, the application of the color + profile may be inaccurate. + + This command is experimental, and may be removed or changed in the future. It + may have no effect or produce unexpected output when used together with future + HDR support features. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 4b7ee999..7c781355 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -294,6 +294,9 @@ void sway_text_node_set_text(struct sway_text_node *node, char *text) { void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (max_width == buffer->props.max_width) { + return; + } buffer->props.max_width = max_width; wlr_scene_buffer_set_dest_size(buffer->buffer_node, get_text_width(&buffer->props), buffer->props.height); @@ -303,6 +306,9 @@ void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (memcmp(&node->background, background, sizeof(*background) * 4) == 0) { + return; + } memcpy(&node->background, background, sizeof(*background) * 4); render_backing_buffer(buffer); } diff --git a/sway/tree/output.c b/sway/tree/output.c index 2d11195e..6c8dd6dc 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -279,6 +279,7 @@ void output_destroy(struct sway_output *output) { list_free(output->workspaces); list_free(output->current.workspaces); wl_event_source_remove(output->repaint_timer); + wlr_color_transform_unref(output->color_transform); free(output); } diff --git a/sway/tree/root.c b/sway/tree/root.c index ae3c3cb2..20fcfa59 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -53,7 +53,7 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); #endif root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); diff --git a/sway/tree/view.c b/sway/tree/view.c index 35b4b73f..4f757acf 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,16 +1,17 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include -#include "config.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #endif #include "list.h" @@ -126,7 +127,7 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND uint32_t view_get_x11_window_id(struct sway_view *view) { if (view->impl->get_int_prop) { return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); @@ -159,7 +160,7 @@ const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; #endif @@ -173,9 +174,9 @@ void view_get_constraints(struct sway_view *view, double *min_width, view->impl->get_constraints(view, min_width, max_width, min_height, max_height); } else { - *min_width = DBL_MIN; + *min_width = 1; *max_width = DBL_MAX; - *min_height = DBL_MIN; + *min_height = 1; *max_height = DBL_MAX; } } @@ -365,8 +366,8 @@ void view_autoconfigure(struct sway_view *view) { con->pending.content_x = x; con->pending.content_y = y; - con->pending.content_width = width; - con->pending.content_height = height; + con->pending.content_width = fmax(width, 1); + con->pending.content_height = fmax(height, 1); } void view_set_activated(struct sway_view *view, bool activated) { @@ -499,7 +500,7 @@ void view_execute_criteria(struct sway_view *view) { static void view_populate_pid(struct sway_view *view) { pid_t pid; switch (view->type) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case SWAY_VIEW_XWAYLAND:; struct wlr_xwayland_surface *surf = wlr_xwayland_surface_try_from_wlr_surface(view->surface); @@ -741,6 +742,14 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, ws = select_workspace(view); } + if (ws && ws->output) { + // Once the output is determined, we can notify the client early about + // scale to reduce startup jitter. + float scale = ws->output->wlr_output->scale; + wlr_fractional_scale_v1_notify_scale(wlr_surface, scale); + wlr_surface_set_preferred_buffer_scale(wlr_surface, ceil(scale)); + } + struct sway_seat *seat = input_manager_current_seat(); struct sway_node *node = seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); @@ -838,7 +847,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, bool set_focus = should_focus(view); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { set_focus &= wlr_xwayland_icccm_input_model(xsurface) != @@ -927,11 +936,14 @@ void view_update_size(struct sway_view *view) { void view_center_and_clip_surface(struct sway_view *view) { struct sway_container *con = view->container; + bool clip_to_geometry = true; + if (container_is_floating(con)) { // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); + clip_to_geometry = !view->using_csd; wlr_scene_node_set_position(&view->content_tree->node, x, y); } else { @@ -940,12 +952,16 @@ void view_center_and_clip_surface(struct sway_view *view) { // only make sure to clip the content if there is content to clip if (!wl_list_empty(&con->view->content_tree->children)) { - wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ - .x = con->view->geometry.x, - .y = con->view->geometry.y, - .width = con->current.content_width, - .height = con->current.content_height, - }); + struct wlr_box clip = {0}; + if (clip_to_geometry) { + clip = (struct wlr_box){ + .x = con->view->geometry.x, + .y = con->view->geometry.y, + .width = con->current.content_width, + .height = con->current.content_height, + }; + } + wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip); } } @@ -954,7 +970,7 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xdg_surface(xdg_surface); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xwayland_surface(xsurface); @@ -1178,7 +1194,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { ipc_event_window(view->container, "urgent"); - if (!container_is_scratchpad_hidden(view->container)) { + if (!container_is_scratchpad_hidden_or_child(view->container)) { workspace_detect_urgent(view->container->pending.workspace); } } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index a68dc927..f8709a4c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -10,6 +10,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -707,6 +708,11 @@ void workspace_for_each_container(struct sway_workspace *ws, struct sway_container *workspace_find_container(struct sway_workspace *ws, bool (*test)(struct sway_container *con, void *data), void *data) { struct sway_container *result = NULL; + if (ws == NULL){ + sway_log(SWAY_ERROR, "Cannot find container with no workspace."); + return NULL; + } + // Tiling for (int i = 0; i < ws->tiling->length; ++i) { struct sway_container *child = ws->tiling->items[i]; diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index b7c80dd4..fd604874 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -38,14 +38,14 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, } // This is an activation request. If this context is internal we have ctx->seat. - struct sway_seat *seat = ctx->seat; - if (!seat) { - // Otherwise, use the seat indicated by the launcher client in set_serial - seat = ctx->token->seat ? ctx->token->seat->data : NULL; + if (ctx->seat) { + view_request_activate(view, ctx->seat); + return; } - if (seat && ctx->had_focused_surface) { - view_request_activate(view, seat); + // Otherwise, activate if passed from another focused client + if (ctx->token->seat && ctx->had_focused_surface) { + view_request_activate(view, ctx->token->seat->data); } else { // The token is valid, but cannot be used to activate a window view_request_urgent(view);