From 82fe097b3109b7869272ccb487a9ed691c788b96 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Sun, 16 Feb 2025 15:33:52 -0500 Subject: [PATCH] feat: sway 1.10 rebase (#367) Co-authored-by: Erik Reider <35975961+ErikReider@users.noreply.github.com> --- README.md | 22 +- client/pool-buffer.c | 1 - common/gesture.c | 1 - common/ipc-client.c | 1 - common/log.c | 1 - common/loop.c | 1 - common/meson.build | 2 - common/pango.c | 3 + common/stringop.c | 1 - common/util.c | 1 - config.in | 40 +- include/background-image.h | 20 - include/sway/commands.h | 16 +- include/sway/config.h | 48 +- include/sway/criteria.h | 6 +- include/sway/desktop.h | 13 - include/sway/desktop/transaction.h | 14 +- include/sway/input/cursor.h | 2 +- include/sway/input/input-manager.h | 8 +- include/sway/input/seat.h | 47 +- include/sway/input/text_input.h | 22 - include/sway/input/text_input_popup.h | 23 + include/sway/layer_criteria.h | 19 +- include/sway/layers.h | 54 +- include/sway/lock.h | 6 + include/sway/output.h | 137 +- include/sway/scene_descriptor.h | 33 + include/sway/server.h | 78 +- include/sway/surface.h | 24 - include/sway/sway_text_node.h | 28 + include/sway/tree/container.h | 116 +- include/sway/tree/node.h | 16 + include/sway/tree/root.h | 46 +- include/sway/tree/view.h | 132 +- include/sway/tree/workspace.h | 10 +- include/sway/xdg_decoration.h | 2 + include/swaybar/image.h | 7 + meson.build | 47 +- meson_options.txt | 1 - protocols/meson.build | 4 +- protocols/wlr-input-inhibitor-unstable-v1.xml | 67 - sway-portals.conf | 6 - sway.desktop | 2 +- sway/commands.c | 7 +- sway/commands/allow_tearing.c | 24 + sway/commands/assign.c | 1 - sway/commands/bar.c | 1 - sway/commands/bar/font.c | 1 - sway/commands/bar/hidden_state.c | 1 - sway/commands/bar/icon_theme.c | 1 - sway/commands/bar/id.c | 1 - sway/commands/bar/mode.c | 1 - sway/commands/bar/output.c | 1 - sway/commands/bar/position.c | 1 - sway/commands/bar/separator_symbol.c | 1 - sway/commands/bar/tray_output.c | 1 - sway/commands/bind.c | 1 - sway/commands/blur.c | 17 +- sway/commands/blur_brightness.c | 17 +- sway/commands/blur_contrast.c | 18 +- sway/commands/blur_noise.c | 35 +- sway/commands/blur_passes.c | 18 +- sway/commands/blur_radius.c | 16 +- sway/commands/blur_saturation.c | 17 +- sway/commands/blur_xray.c | 7 +- sway/commands/client.c | 12 +- sway/commands/corner_radius.c | 15 +- sway/commands/default_dim_inactive.c | 9 +- sway/commands/dim_inactive.c | 9 +- sway/commands/dim_inactive_colors.c | 9 +- sway/commands/exec_always.c | 1 - sway/commands/font.c | 1 - sway/commands/gaps.c | 6 +- sway/commands/gesture.c | 1 - sway/commands/input.c | 3 +- sway/commands/input/calibration_matrix.c | 1 - sway/commands/input/clickfinger_button_map.c | 27 + sway/commands/input/events.c | 1 + sway/commands/input/map_from_region.c | 1 - sway/commands/input/map_to_output.c | 1 - sway/commands/input/map_to_region.c | 1 - sway/commands/input/xkb_file.c | 1 - sway/commands/input/xkb_layout.c | 1 - sway/commands/input/xkb_model.c | 1 - sway/commands/input/xkb_numlock.c | 1 - sway/commands/input/xkb_options.c | 1 - sway/commands/input/xkb_rules.c | 1 - sway/commands/input/xkb_switch_layout.c | 12 +- sway/commands/input/xkb_variant.c | 1 - sway/commands/layer_effects.c | 51 +- sway/commands/mark.c | 3 +- sway/commands/mode.c | 1 - sway/commands/move.c | 13 +- sway/commands/opacity.c | 3 +- sway/commands/output.c | 25 +- sway/commands/output/adaptive_sync.c | 24 +- sway/commands/output/allow_tearing.c | 23 + sway/commands/output/background.c | 1 - sway/commands/output/color_profile.c | 113 + sway/commands/output/render_bit_depth.c | 7 +- sway/commands/output/toggle.c | 2 +- sway/commands/output/transform.c | 1 + sway/commands/primary_selection.c | 4 +- sway/commands/reload.c | 8 +- sway/commands/saturation.c | 43 - sway/commands/scratchpad_minimize.c | 2 - sway/commands/seat/attach.c | 1 - sway/commands/seat/cursor.c | 16 +- sway/commands/seat/hide_cursor.c | 1 - sway/commands/seat/idle.c | 1 - sway/commands/seat/pointer_constraint.c | 1 + sway/commands/seat/shortcuts_inhibitor.c | 1 + sway/commands/seat/xcursor_theme.c | 1 - sway/commands/set.c | 1 - sway/commands/shadows.c | 7 +- sway/commands/shortcuts_inhibitor.c | 1 + sway/commands/show_marks.c | 12 +- sway/commands/swap.c | 5 +- sway/commands/title_align.c | 9 +- sway/commands/title_format.c | 17 +- sway/commands/titlebar_border_thickness.c | 1 - sway/commands/titlebar_padding.c | 26 +- sway/commands/titlebar_separator.c | 1 - sway/commands/unmark.c | 12 +- sway/commands/workspace.c | 1 - sway/commands/xwayland.c | 4 +- sway/config.c | 143 +- sway/config/bar.c | 2 +- sway/config/input.c | 6 +- sway/config/output.c | 1045 ++++++---- sway/config/seat.c | 1 - sway/criteria.c | 20 +- sway/desktop/desktop.c | 40 - sway/desktop/launcher.c | 2 +- sway/desktop/layer_shell.c | 890 ++++---- sway/desktop/output.c | 1292 ++++-------- sway/desktop/render.c | 1848 ----------------- sway/desktop/surface.c | 72 - sway/desktop/tearing.c | 64 + sway/desktop/transaction.c | 619 +++++- sway/desktop/xdg_shell.c | 222 +- sway/desktop/xwayland.c | 128 +- sway/input/cursor.c | 296 +-- sway/input/input-manager.c | 75 +- sway/input/keyboard.c | 104 +- sway/input/libinput.c | 19 +- sway/input/seat.c | 172 +- sway/input/seatop_default.c | 71 +- sway/input/seatop_down.c | 12 +- sway/input/seatop_move_floating.c | 6 +- sway/input/seatop_move_tiling.c | 110 +- sway/input/seatop_resize_floating.c | 3 +- sway/input/seatop_resize_tiling.c | 3 +- sway/input/switch.c | 10 +- sway/input/tablet.c | 2 +- sway/input/text_input.c | 628 +++--- sway/ipc-json.c | 168 +- sway/ipc-server.c | 7 +- sway/layer_criteria.c | 122 +- sway/lock.c | 364 ++-- sway/main.c | 60 +- sway/meson.build | 23 +- sway/scene_descriptor.c | 69 + sway/server.c | 155 +- sway/sway-input.5.scd | 6 + sway/sway-ipc.7.scd | 34 +- sway/sway-output.5.scd | 52 +- sway/sway.5.scd | 46 +- sway/sway_text_node.c | 314 +++ sway/swaynag.c | 1 - sway/tree/arrange.c | 16 +- sway/tree/container.c | 1164 ++++++----- sway/tree/node.c | 48 +- sway/tree/output.c | 77 +- sway/tree/root.c | 70 +- sway/tree/view.c | 630 ++---- sway/tree/workspace.c | 83 +- sway/xdg_activation_v1.c | 13 +- sway/xdg_decoration.c | 77 +- swaybar/bar.c | 3 +- swaybar/config.c | 1 - swaybar/i3bar.c | 1 - common/background-image.c => swaybar/image.c | 92 +- swaybar/ipc.c | 4 +- swaybar/main.c | 1 - swaybar/meson.build | 1 + swaybar/render.c | 1 - swaybar/status_line.c | 1 - swaybar/tray/host.c | 1 - swaybar/tray/icon.c | 1 - swaybar/tray/item.c | 5 +- swaybar/tray/tray.c | 12 +- swaybar/tray/watcher.c | 13 +- swaymsg/main.c | 16 +- swaynag/config.c | 1 - swaynag/main.c | 1 - swaynag/swaynag.c | 1 - swaynag/types.c | 1 - 198 files changed, 5953 insertions(+), 7584 deletions(-) delete mode 100644 include/background-image.h delete mode 100644 include/sway/desktop.h create mode 100644 include/sway/input/text_input_popup.h create mode 100644 include/sway/lock.h create mode 100644 include/sway/scene_descriptor.h delete mode 100644 include/sway/surface.h create mode 100644 include/sway/sway_text_node.h create mode 100644 include/swaybar/image.h delete mode 100644 protocols/wlr-input-inhibitor-unstable-v1.xml delete mode 100644 sway-portals.conf create mode 100644 sway/commands/allow_tearing.c create mode 100644 sway/commands/input/clickfinger_button_map.c create mode 100644 sway/commands/output/allow_tearing.c create mode 100644 sway/commands/output/color_profile.c delete mode 100644 sway/commands/saturation.c delete mode 100644 sway/desktop/desktop.c delete mode 100644 sway/desktop/render.c delete mode 100644 sway/desktop/surface.c create mode 100644 sway/desktop/tearing.c create mode 100644 sway/scene_descriptor.c create mode 100644 sway/sway_text_node.c rename common/background-image.c => swaybar/image.c (56%) diff --git a/README.md b/README.md index 50670e5b..efea3f9e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Sway is an incredible window manager, and certainly one of the most well establi + **Anti-aliased rounded corners, borders, and titlebars** + **Shadows** + **Dim unfocused windows** -+ **Per application saturation control**: Allows the user to set the saturation (Digital Vibrance) for specific applications. Great for some FPS games! + **Scratchpad treated as minimize**: Allows docks, or panels with a taskbar, to correctly interpret minimize / unminimize requests ([thanks to LCBCrion](https://github.com/swaywm/sway/issues/6457)) + **nixify the repo**: Allows nixos users to easily contribute to and test this project @@ -47,18 +46,29 @@ Sway is an incredible window manager, and certainly one of the most well establi - The current layer namespaces can be shown with `swaymsg -r -t get_outputs | jq '.[0].layer_shell_surfaces | .[] | .namespace'` - Example: `layer_effects "waybar" blur enable; shadows enable; corner_radius 6` - Note: If an application uses gtk, its namespace is likely to be "gtk-layer-shell" - - SwayIPC Example: `swaymsg "layer_effects 'waybar' 'blur enable; shadows enable; corner_radius 6'"` + - SwayIPC Example: `swaymsg layer_effects "waybar" "blur enable"` (you can only set one effect at a time through `swaymsg`) + - Config Example: + ``` + layer_effects "waybar" { + blur enable; + blur_xray enable; + blur_ignore_transparent enable; + shadows enable; + corner_radius 20; + } + ``` - Available Effects: - `blur ` + - `blur_xray ` - `blur_ignore_transparent ` - `shadows ` - `corner_radius ` + - `reset`: To reset/disable all previously applied effects to the layer application + Dim unfocused windows: - `default_dim_inactive ` - `for_window [CRITERIA_HERE] dim_inactive ` - `dim_inactive_colors.unfocused ex, #000000FF` - `dim_inactive_colors.urgent ex, #900000FF` -+ Application saturation: `for_window [CRITERIA HERE] saturation 2.0>` + Keep/remove separator border between titlebar and content: `titlebar_separator enable|disable` + Treat Scratchpad as minimized: `scratchpad_minimize enable|disable`: **we recommend keeping this setting off, as there are many kinks to iron out here** @@ -123,12 +133,6 @@ SwayFX will drop root permissions shortly after startup. SwayFX would love to receive any new features that you're willing to build! Generally, we'd like to focus on eye-candy type improvements to keep our scope appropriate. If you'd like to build something that you think may be out of that focus, please raise an issue and we can discuss whether or not it will fit within this project. -Here's a quick outline of where most of our changes lie vs the main sway repository: - -+ `sway/desktop/render.c`: the file that handles calling `fx_renderer` to render to the screen, handles damage tracking and scaling -+ `sway/desktop/fx_renderer/fx_renderer.c`: the meat and potatoes of this project, structured as similarly to wlr_renderer as possible -+ `sway/desktop/fx_renderer/shaders`: where all of the shaders that fx_renderer uses live - ## Acknowledgements The SwayFX team would like to first and foremost thank the maintainers and contributors of the Sway window manager. We are but a humble group of Sway enthusiasts who wanted to expand upon your creation. diff --git a/client/pool-buffer.c b/client/pool-buffer.c index 3546b897..c47c40eb 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809 #include #include #include diff --git a/common/gesture.c b/common/gesture.c index 58170443..272aa837 100644 --- a/common/gesture.c +++ b/common/gesture.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "gesture.h" #include diff --git a/common/ipc-client.c b/common/ipc-client.c index d30212d2..a0be2b2d 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/common/log.c b/common/log.c index 483420e7..3eacdb34 100644 --- a/common/log.c +++ b/common/log.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include diff --git a/common/loop.c b/common/loop.c index 80fe18ea..b99c6d55 100644 --- a/common/loop.c +++ b/common/loop.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200112L #include #include #include diff --git a/common/meson.build b/common/meson.build index 3756075a..c0ce1f68 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,7 +1,6 @@ lib_sway_common = static_library( 'sway-common', files( - 'background-image.c', 'cairo.c', 'gesture.c', 'ipc-client.c', @@ -14,7 +13,6 @@ lib_sway_common = static_library( ), dependencies: [ cairo, - gdk_pixbuf, pango, pangocairo, wayland_client.partial_dependency(compile_args: true) 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/common/stringop.c b/common/stringop.c index c503143a..16d04917 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/common/util.c b/common/util.c index 5d4c0673..7c492bcb 100644 --- a/common/util.c +++ b/common/util.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/config.in b/config.in index d478178e..a2a01dda 100644 --- a/config.in +++ b/config.in @@ -16,32 +16,7 @@ set $right l # Your preferred terminal emulator set $term foot # Your preferred application launcher -# Note: pass the final command to swaymsg so that the resulting window can be opened -# on the original workspace that the command was run on. -set $menu dmenu_path | wmenu | xargs swaymsg exec -- - -### Appearance -# window corner radius in px -corner_radius 10 - -# Window background blur -blur off -blur_xray off -blur_passes 2 -blur_radius 5 - -shadows off -shadows_on_csd off -shadow_blur_radius 20 -shadow_color #0000007F - -# inactive window fade amount. 0.0 = no dimming, 1.0 = fully dimmed -default_dim_inactive 0.0 -dim_inactive_colors.unfocused #000000FF -dim_inactive_colors.urgent #900000FF - -# Move minimized windows into Scratchpad (enable|disable) -scratchpad_minimize disable +set $menu wmenu-run ### Output configuration # @@ -218,6 +193,19 @@ mode "resize" { bindsym Escape mode "default" } bindsym $mod+r mode "resize" +# +# Utilities: +# + # Special keys to adjust volume via PulseAudio + bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle + bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% + bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% + bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + # Special keys to adjust brightness via brightnessctl + bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- + bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ + # Special key to take a screenshot with grim + bindsym Print exec grim # # Status Bar: diff --git a/include/background-image.h b/include/background-image.h deleted file mode 100644 index a97ef375..00000000 --- a/include/background-image.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _SWAY_BACKGROUND_IMAGE_H -#define _SWAY_BACKGROUND_IMAGE_H -#include "cairo_util.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 diff --git a/include/sway/commands.h b/include/sway/commands.h index 8f21b989..bcb31ba6 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -18,9 +18,9 @@ struct cmd_handler { * Indicates the result of a command's execution. */ enum cmd_status { - CMD_SUCCESS, /**< The command was successful */ + CMD_SUCCESS, /**< The command was successful */ CMD_FAILURE, /**< The command resulted in an error */ - CMD_INVALID, /**< Unknown command or parser error */ + CMD_INVALID, /**< Unknown command or parser error */ CMD_DEFER, /**< Command execution deferred */ CMD_BLOCK, CMD_BLOCK_COMMANDS, @@ -97,7 +97,6 @@ void container_resize_tiled(struct sway_container *parent, uint32_t axis, struct sway_container *container_find_resize_parent(struct sway_container *con, uint32_t edge); - /** * Effect handlers value parsers */ @@ -109,6 +108,7 @@ bool cmd_corner_radius_parse_value(char *arg, int* result); sway_cmd cmd_exec_validate; sway_cmd cmd_exec_process; +sway_cmd cmd_allow_tearing; sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; @@ -175,11 +175,10 @@ sway_cmd cmd_max_render_time; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; -sway_cmd cmd_nop; -sway_cmd cmd_opacity; -sway_cmd cmd_saturation; sway_cmd cmd_new_float; sway_cmd cmd_new_window; +sway_cmd cmd_nop; +sway_cmd cmd_opacity; sway_cmd cmd_no_focus; sway_cmd cmd_output; sway_cmd cmd_permit; @@ -193,13 +192,13 @@ sway_cmd cmd_scratchpad; sway_cmd cmd_scratchpad_minimize; sway_cmd cmd_seamless_mouse; sway_cmd cmd_set; -sway_cmd cmd_shortcuts_inhibitor; sway_cmd cmd_shadow_blur_radius; sway_cmd cmd_shadow_color; sway_cmd cmd_shadow_offset; sway_cmd cmd_shadow_inactive_color; sway_cmd cmd_shadows; sway_cmd cmd_shadows_on_csd; +sway_cmd cmd_shortcuts_inhibitor; sway_cmd cmd_show_marks; sway_cmd cmd_smart_borders; sway_cmd cmd_smart_corner_radius; @@ -278,6 +277,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; @@ -311,7 +311,9 @@ sway_cmd input_cmd_xkb_switch_layout; sway_cmd input_cmd_xkb_variant; sway_cmd output_cmd_adaptive_sync; +sway_cmd output_cmd_allow_tearing; 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 84e4ab8e..6fe8cf2d 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -1,12 +1,14 @@ #ifndef _SWAY_CONFIG_H #define _SWAY_CONFIG_H #include +#include #include #include #include #include #include #include +#include #include #include #include "../include/config.h" @@ -15,7 +17,6 @@ #include "stringop.h" #include "swaynag.h" #include "tree/container.h" -#include "scenefx/types/fx/blur_data.h" #include "sway/input/tablet.h" #include "sway/tree/root.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -149,6 +150,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; @@ -261,6 +263,7 @@ enum scale_filter_mode { enum render_bit_depth { RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 + RENDER_BIT_DEPTH_6, RENDER_BIT_DEPTH_8, RENDER_BIT_DEPTH_10, }; @@ -286,6 +289,9 @@ 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; + int allow_tearing; char *background; char *background_option; @@ -487,6 +493,10 @@ struct sway_config { float urgent[4]; } dim_inactive_colors; + bool blur_enabled; + bool blur_xray; + struct blur_data blur_data; + bool shadow_enabled; bool shadows_on_csd_enabled; int shadow_blur_sigma; @@ -494,10 +504,6 @@ struct sway_config { float shadow_inactive_color[4]; float shadow_offset_x, shadow_offset_y; - bool blur_enabled; - bool blur_xray; - struct blur_data blur_params; - bool titlebar_separator; bool scratchpad_minimize; @@ -706,22 +712,28 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); -void merge_output_config(struct output_config *dst, struct output_config *src); +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off); -bool apply_output_config(struct output_config *oc, struct sway_output *output); +void apply_stored_output_configs(void); -bool test_output_config(struct output_config *oc, struct sway_output *output); - -struct output_config *store_output_config(struct output_config *oc); +/** + * store_output_config stores a new output config. An output may be matched by + * three different config types, in order of precedence: Identifier, name and + * wildcard. When storing a config type of lower precedence, assume that the + * user wants the config to take immediate effect by superseding (clearing) the + * same values from higher presedence configuration. + */ +void store_output_config(struct output_config *oc); struct output_config *find_output_config(struct sway_output *output); -void apply_output_config_to_outputs(struct output_config *oc); - -void reset_outputs(void); - void free_output_config(struct output_config *oc); +void request_modeset(void); +void force_modeset(void); +bool modeset_is_pending(void); + bool spawn_swaybg(void); int workspace_output_cmp_workspace(const void *a, const void *b); @@ -765,14 +777,6 @@ void translate_keysyms(struct input_config *input_config); void binding_add_translated(struct sway_binding *binding, list_t *bindings); -int config_get_blur_size(); - -bool config_should_parameters_blur(); - -bool config_should_parameters_blur_effects(); - -bool config_should_parameters_shadow(); - /* Global config singleton. */ extern struct sway_config *config; 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/desktop.h b/include/sway/desktop.h deleted file mode 100644 index 7f2f5b3e..00000000 --- a/include/sway/desktop.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -struct sway_container; -struct sway_view; - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole); - -void desktop_damage_whole_container(struct sway_container *con); - -void desktop_damage_box(struct wlr_box *box); - -void desktop_damage_view(struct sway_view *view); diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7dd58ba8..0159132d 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,6 +1,8 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H +#include #include +#include /** * Transactions enable us to perform atomic layout updates. @@ -38,8 +40,11 @@ void transaction_commit_dirty_client(void); * Notify the transaction system that a view is ready for the new layout. * * When all views in the transaction are ready, the layout will be applied. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial); /** @@ -47,8 +52,13 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, * identifying the instruction by geometry rather than by serial. * * This is used by xwayland views, as they don't have serials. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height); +void arrange_popups(struct wlr_scene_tree *popups); + #endif diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 1e21c66f..527d0350 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -114,7 +114,7 @@ void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, - enum wlr_button_state state); + enum wl_pointer_button_state state); void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_pointer_axis_event *event); diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 4bd51709..b014e18f 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -1,14 +1,15 @@ #ifndef _SWAY_INPUT_INPUT_MANAGER_H #define _SWAY_INPUT_INPUT_MANAGER_H #include -#include #include #include #include -#include "sway/server.h" +#include #include "sway/config.h" #include "list.h" +struct sway_server; + struct sway_input_device { char *identifier; struct wlr_input_device *wlr_device; @@ -21,11 +22,11 @@ struct sway_input_manager { struct wl_list devices; struct wl_list seats; - struct wlr_input_inhibit_manager *inhibit; struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wlr_virtual_pointer_manager_v1 *virtual_pointer; struct wlr_pointer_gestures_v1 *pointer_gestures; + struct wlr_transient_seat_manager_v1 *transient_seat_manager; struct wl_listener new_input; struct wl_listener inhibit_activate; @@ -33,6 +34,7 @@ struct sway_input_manager { struct wl_listener keyboard_shortcuts_inhibit_new_inhibitor; struct wl_listener virtual_keyboard_new; struct wl_listener virtual_pointer_new; + struct wl_listener transient_seat_create; }; struct sway_input_manager *input_manager_create(struct sway_server *server); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 6063ead3..72b3cd90 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -1,6 +1,7 @@ #ifndef _SWAY_INPUT_SEAT_H #define _SWAY_INPUT_SEAT_H +#include #include #include #include @@ -12,12 +13,11 @@ #include "sway/input/text_input.h" struct sway_seat; -struct fx_render_context; struct sway_seatop_impl { void (*button)(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state); + enum wl_pointer_button_state state); void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); void (*pointer_axis)(struct sway_seat *seat, struct wlr_pointer_axis_event *event); @@ -52,7 +52,6 @@ struct sway_seatop_impl { uint32_t time_msec, enum wlr_tablet_tool_tip_state state); void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); - void (*render)(struct sway_seat *seat, struct fx_render_context *ctx); bool allow_set_cursor; }; @@ -75,20 +74,6 @@ struct sway_seat_node { struct wl_listener destroy; }; -struct sway_drag_icon { - struct sway_seat *seat; - struct wlr_drag_icon *wlr_drag_icon; - struct wl_list link; // sway_root::drag_icons - - double x, y; // in layout-local coordinates - int dx, dy; // offset in surface-local coordinates - - struct wl_listener surface_commit; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; -}; - struct sway_drag { struct sway_seat *seat; struct wlr_drag *wlr_drag; @@ -99,6 +84,15 @@ struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; + // Seat scene tree structure + // - scene_tree + // - drag icons + // - drag icon 1 + // - drag icon 2 + // - seatop specific stuff + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *drag_icons; + bool has_focus; struct wl_list focus_stack; // list of containers in focus order struct sway_workspace *workspace; @@ -108,9 +102,6 @@ struct sway_seat { // If the exclusive layer is set, views cannot receive keyboard focus bool has_exclusive_layer; - // If exclusive_client is set, no other clients will receive input events - struct wl_client *exclusive_client; - // Last touch point int32_t touch_id; double touch_x, touch_y; @@ -133,6 +124,7 @@ struct sway_seat { struct wl_listener start_drag; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; + struct wl_listener destroy; struct wl_list devices; // sway_seat_device::link struct wl_list keyboard_groups; // sway_keyboard_group::link @@ -201,8 +193,7 @@ void seat_set_focus_surface(struct sway_seat *seat, void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface_v1 *layer); -void seat_set_exclusive_client(struct sway_seat *seat, - struct wl_client *client); +void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client); struct sway_node *seat_get_focus(struct sway_seat *seat); @@ -261,7 +252,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); -void drag_icon_update_position(struct sway_drag_icon *icon); +void drag_icons_update_position(struct sway_seat *seat); enum wlr_edges find_resize_edge(struct sway_container *cont, struct wlr_surface *surface, struct sway_cursor *cursor); @@ -296,13 +287,13 @@ struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_workspace *workspace); void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, - uint32_t button, enum wlr_button_state state); + uint32_t button, enum wl_pointer_button_state state); void seat_consider_warp_to_focus(struct sway_seat *seat); void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state); + enum wl_pointer_button_state state); void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); @@ -361,12 +352,6 @@ void seatop_end(struct sway_seat *seat); */ void seatop_unref(struct sway_seat *seat, struct sway_container *con); -/** - * Instructs a seatop to render anything that it needs to render - * (eg. dropzone for move-tiling) - */ -void seatop_render(struct sway_seat *seat, struct fx_render_context *ctx); - bool seatop_allows_set_cursor(struct sway_seat *seat); /** diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index f583af7a..1993f928 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -4,7 +4,6 @@ #include #include #include -#include /** * The relay structure manages the relationship between text-input and @@ -36,22 +35,6 @@ struct sway_input_method_relay { struct wl_listener input_method_keyboard_grab_destroy; }; -struct sway_input_popup { - struct sway_input_method_relay *relay; - struct wlr_input_popup_surface_v2 *popup_surface; - - int x, y; - bool visible; - - struct wl_list link; - - struct wl_listener popup_map; - struct wl_listener popup_unmap; - struct wl_listener popup_destroy; - struct wl_listener popup_surface_commit; - - struct wl_listener focused_surface_unmap; -}; struct sway_text_input { struct sway_input_method_relay *relay; @@ -85,9 +68,4 @@ struct sway_text_input *sway_text_input_create( struct sway_input_method_relay *relay, struct wlr_text_input_v3 *text_input); -bool sway_input_popup_get_position( - struct sway_input_popup *popup, int *lx, int *ly); - -void sway_input_popup_damage(struct sway_input_popup *popup); - #endif diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h new file mode 100644 index 00000000..7e838ed2 --- /dev/null +++ b/include/sway/input/text_input_popup.h @@ -0,0 +1,23 @@ +#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H +#define _SWAY_INPUT_TEXT_INPUT_POPUP_H + +#include "sway/tree/view.h" + +struct sway_input_popup { + struct sway_input_method_relay *relay; + + struct wlr_scene_tree *scene_tree; + struct sway_popup_desc desc; + struct wlr_input_popup_surface_v2 *popup_surface; + struct wlr_output *fixed_output; + + struct wl_list link; + + struct wl_listener popup_destroy; + struct wl_listener popup_surface_commit; + struct wl_listener popup_surface_map; + struct wl_listener popup_surface_unmap; + + struct wl_listener focused_surface_unmap; +}; +#endif diff --git a/include/sway/layer_criteria.h b/include/sway/layer_criteria.h index f0906460..554a482d 100644 --- a/include/sway/layer_criteria.h +++ b/include/sway/layer_criteria.h @@ -1,20 +1,19 @@ #include -#include "sway/layers.h" -#include "sway/config.h" struct layer_criteria { char *namespace; char *cmdlist; + + bool shadow_enabled; + bool blur_enabled; + bool blur_xray; + bool blur_ignore_transparent; + int corner_radius; }; void layer_criteria_destroy(struct layer_criteria *criteria); -bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b); +struct layer_criteria *layer_criteria_add(char *namespace, char *cmdlist); -bool layer_criteria_already_exists(struct layer_criteria *criteria); - -// Gathers all of the matching criterias for a specified `sway_layer_surface` -list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer); - -// Parses the `layer_criteria` and applies the effects to the `sway_layer_surface` -void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria); +// Get the matching criteria for a specified `sway_layer_surface` +struct layer_criteria *layer_criteria_for_namespace(char *namespace); diff --git a/include/sway/layers.h b/include/sway/layers.h index bb7cbb22..edc7e65e 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -3,59 +3,42 @@ #include #include #include - -enum layer_parent { - LAYER_PARENT_LAYER, - LAYER_PARENT_POPUP, -}; +#include "sway/layer_criteria.h" +#include "sway/tree/view.h" struct sway_layer_surface { - struct wlr_layer_surface_v1 *layer_surface; - struct wl_list link; - - struct wl_listener destroy; struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; + struct wl_listener node_destroy; struct wl_listener new_popup; - struct wl_listener new_subsurface; - struct wlr_box geo; bool mapped; - struct wlr_box extent; - enum zwlr_layer_shell_v1_layer layer; - struct wl_list subsurfaces; + struct wlr_scene_tree *popups; + struct sway_popup_desc desc; - bool has_shadow; - bool has_blur; + struct sway_output *output; + struct wlr_scene_layer_surface_v1 *scene; + struct wlr_scene_tree *tree; + struct wlr_scene_shadow *shadow_node; + struct wlr_layer_surface_v1 *layer_surface; + + bool shadow_enabled; + bool blur_enabled; + bool blur_xray; bool blur_ignore_transparent; int corner_radius; }; struct sway_layer_popup { struct wlr_xdg_popup *wlr_popup; - enum layer_parent parent_type; - union { - struct sway_layer_surface *parent_layer; - struct sway_layer_popup *parent_popup; - }; - struct wl_listener map; - struct wl_listener unmap; + struct wlr_scene_tree *scene; + struct sway_layer_surface *toplevel; + struct wl_listener destroy; - struct wl_listener commit; struct wl_listener new_popup; -}; - -struct sway_layer_subsurface { - struct wlr_subsurface *wlr_subsurface; - struct sway_layer_surface *layer_surface; - struct wl_list link; - - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; struct wl_listener commit; }; @@ -66,7 +49,6 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( void arrange_layers(struct sway_output *output); -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface); +void layer_apply_criteria(struct sway_layer_surface *surface, struct layer_criteria *criteria); #endif diff --git a/include/sway/lock.h b/include/sway/lock.h new file mode 100644 index 00000000..5be0f969 --- /dev/null +++ b/include/sway/lock.h @@ -0,0 +1,6 @@ +#ifndef _SWAY_LOCK_H +#define _SWAY_LOCK_H + +void arrange_locks(void); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index 0e9e27c9..06e7865f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -1,40 +1,18 @@ #ifndef _SWAY_OUTPUT_H #define _SWAY_OUTPUT_H +#include #include #include #include #include #include #include "config.h" -#include "scenefx/render/pass.h" #include "sway/tree/node.h" #include "sway/tree/view.h" -struct decoration_data get_undecorated_decoration_data(); - struct sway_server; struct sway_container; -struct decoration_data { - float alpha; - float saturation; - int corner_radius; - float dim; - float *dim_color; - bool has_titlebar; - bool discard_transparent; - bool blur; - bool shadow; -}; - -struct render_data { - struct fx_render_context *ctx; - pixman_region32_t *damage; - struct wlr_box *clip_box; - struct decoration_data deco_data; - struct sway_view *view; -}; - struct sway_output_state { list_t *workspaces; struct sway_workspace *active_workspace; @@ -42,22 +20,39 @@ struct sway_output_state { struct sway_output { struct sway_node node; + + struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; + // Used for optimized blur. Everything exclusively below gets blurred + struct wlr_scene_optimized_blur *blur_layer; + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *shell_overlay; + struct wlr_scene_tree *session_lock; + } layers; + + // when a container is fullscreen, in case the fullscreen surface is + // translucent (can see behind) we must make sure that the background is a + // solid color in order to conform to the wayland protocol. This rect + // ensures that when looking through a surface, all that will be seen + // is black. + struct wlr_scene_rect *fullscreen_background; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; struct sway_server *server; struct wl_list link; - struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; - struct timespec last_frame; - struct wlr_damage_ring damage_ring; - int lx, ly; // layout coords int width, height; // transformed buffer size enum wl_output_subpixel detected_subpixel; enum scale_filter_mode scale_filter; - bool enabling, enabled; + bool enabled; list_t *workspaces; struct sway_output_state current; @@ -66,20 +61,21 @@ struct sway_output { struct wl_listener destroy; struct wl_listener commit; struct wl_listener present; - struct wl_listener damage; struct wl_listener frame; - struct wl_listener needs_frame; struct wl_listener request_state; struct { 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 struct wl_event_source *repaint_timer; bool gamma_lut_changed; + bool allow_tearing; }; struct sway_output_non_desktop { @@ -88,14 +84,6 @@ struct sway_output_non_desktop { struct wl_listener destroy; }; -struct fx_render_context { - struct sway_output *output; - struct wlr_renderer *renderer; - pixman_region32_t *output_damage; - - struct fx_gles_render_pass *pass; -}; - struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); @@ -114,19 +102,6 @@ typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, void *user_data); -void output_damage_whole(struct sway_output *output); - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole); - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view); - -void output_damage_box(struct sway_output *output, struct wlr_box *box); - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con); - bool output_match_name_or_id(struct sway_output *output, const char *name_or_id); @@ -142,50 +117,8 @@ void output_enable(struct sway_output *output); void output_disable(struct sway_output *output); -bool output_has_opaque_overlay_layer_surface(struct sway_output *output); - struct sway_workspace *output_get_active_workspace(struct sway_output *output); -void output_render(struct fx_render_context *ctx); - -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data); - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_view_for_each_popup_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data); -#endif - -void output_input_popups_for_each_surface(struct sway_output *output, - struct wl_list *input_popups, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data); - void output_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); @@ -203,24 +136,8 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, - float color[static 4]); - -void render_rounded_rect(struct fx_render_context *ctx, const struct wlr_box *_box, - float color[static 4], int corner_radius, enum corner_location corner_location); - -void render_blur(struct fx_render_context *ctx, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - bool optimized_blur, pixman_region32_t *opaque_region, - struct decoration_data deco_data); - -void premultiply_alpha(float color[4], float opacity); - -void scale_box(struct wlr_box *box, float scale); - enum wlr_direction opposite_direction(enum wlr_direction d); -void handle_output_layout_change(struct wl_listener *listener, void *data); void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); @@ -233,4 +150,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); +void update_output_manager_config(struct sway_server *server); + #endif diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h new file mode 100644 index 00000000..91cc7c70 --- /dev/null +++ b/include/sway/scene_descriptor.h @@ -0,0 +1,33 @@ +/** + * Across a wayland compositor, there are multiple shells: It can be + * a toplevel, or a layer_shell, or even something more meta like a drag + * icon or highlight indicators when dragging windows around. + * + * This object lets us store values that represent these modes of operation + * and keep track of what object is being represented. + */ +#ifndef _SWAY_SCENE_DESCRIPTOR_H +#define _SWAY_SCENE_DESCRIPTOR_H +#include + +enum sway_scene_descriptor_type { + SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_NON_INTERACTIVE, + SWAY_SCENE_DESC_CONTAINER, + SWAY_SCENE_DESC_VIEW, + SWAY_SCENE_DESC_LAYER_SHELL, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, + SWAY_SCENE_DESC_POPUP, + SWAY_SCENE_DESC_DRAG_ICON, +}; + +bool scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data); + +void *scene_descriptor_try_get(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type); + +void scene_descriptor_destroy(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index ef70ae4e..ccf4a9cc 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -2,31 +2,28 @@ #define _SWAY_SERVER_H #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #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 struct sway_transaction; +struct sway_session_lock { + struct wlr_session_lock_v1 *lock; + struct wlr_surface *focused; + bool abandoned; + + struct wl_list outputs; // struct sway_session_lock_output + + // invalid if the session is abandoned + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +}; + struct sway_server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; @@ -40,7 +37,6 @@ struct sway_server { struct wlr_allocator *allocator; struct wlr_compositor *compositor; - struct wl_listener compositor_new_surface; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; @@ -49,7 +45,7 @@ struct sway_server { struct sway_input_manager *input; struct wl_listener new_output; - struct wl_listener output_layout_change; + struct wl_listener renderer_lost; struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; @@ -58,11 +54,11 @@ struct sway_server { struct wl_listener layer_shell_surface; struct wlr_xdg_shell *xdg_shell; - struct wl_listener xdg_shell_surface; + struct wl_listener xdg_shell_toplevel; 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,11 +77,11 @@ struct sway_server { struct wlr_drm_lease_v1_manager *drm_lease_manager; struct wl_listener drm_lease_request; - struct wlr_presentation *presentation; - 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; @@ -94,15 +90,9 @@ struct sway_server { struct wl_listener gamma_control_set_gamma; struct { - bool locked; + struct sway_session_lock *lock; struct wlr_session_lock_manager_v1 *manager; - struct wlr_session_lock_v1 *lock; - struct wlr_surface *focused; - struct wl_listener lock_new_surface; - struct wl_listener lock_unlock; - struct wl_listener lock_destroy; - struct wl_listener new_lock; struct wl_listener manager_destroy; } session_lock; @@ -111,6 +101,7 @@ struct sway_server { struct wl_listener output_power_manager_set_mode; struct wlr_input_method_manager_v2 *input_method; struct wlr_text_input_manager_v3 *text_input; + struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; struct wlr_content_type_manager_v1 *content_type_manager_v1; struct wlr_data_control_manager_v1 *data_control_manager_v1; @@ -123,6 +114,10 @@ struct sway_server { struct wl_listener xdg_activation_v1_new_token; struct wl_listener request_set_cursor_shape; + + struct wlr_tearing_control_manager_v1 *tearing_control_v1; + struct wl_listener tearing_control_new_object; + struct wl_list tearing_controllers; // sway_tearing_controller::link struct wl_list pending_launcher_ctxs; // launcher_ctx::link @@ -143,6 +138,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; @@ -151,17 +148,13 @@ struct sway_debug { bool noatomic; // Ignore atomic layout updates bool txn_timings; // Log verbose messages about transactions bool txn_wait; // Always wait for the timeout before applying - bool noscanout; // Disable direct scan-out - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; + bool legacy_wl_drm; // Enable the legacy wl_drm interface }; extern struct sway_debug debug; +extern bool allow_unsupported_gpu; + bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); @@ -169,14 +162,17 @@ void server_run(struct sway_server *server); void restore_nofile_limit(void); -void handle_compositor_new_surface(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void sway_session_lock_init(void); -void handle_xdg_shell_surface(struct wl_listener *listener, void *data); -#if HAVE_XWAYLAND +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output); +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 WLR_HAS_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); #endif void handle_server_decoration(struct wl_listener *listener, void *data); @@ -189,4 +185,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void set_rr_scheduling(void); +void handle_new_tearing_hint(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index a7a8ec3f..00000000 --- a/include/sway/surface.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _SWAY_SURFACE_H -#define _SWAY_SURFACE_H -#include - -struct sway_surface { - struct wlr_surface *wlr_surface; - - struct wl_listener destroy; - - /** - * This timer can be used for issuing delayed frame done callbacks (for - * example, to improve presentation latency). Its handler is set to a - * function that issues a frame done callback to this surface. - */ - struct wl_event_source *frame_done_timer; -}; - -void surface_update_outputs(struct wlr_surface *surface); -void surface_enter_output(struct wlr_surface *surface, - struct sway_output *output); -void surface_leave_output(struct wlr_surface *surface, - struct sway_output *output); - -#endif diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h new file mode 100644 index 00000000..621cee13 --- /dev/null +++ b/include/sway/sway_text_node.h @@ -0,0 +1,28 @@ +#ifndef _SWAY_BUFFER_H +#define _SWAY_BUFFER_H +#include + +struct sway_text_node { + int width; + int max_width; + int height; + int baseline; + bool pango_markup; + float color[4]; + float background[4]; + + struct wlr_scene_node *node; +}; + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, float color[4], bool pango_markup); + +void sway_text_node_set_color(struct sway_text_node *node, float color[4]); + +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); + +void sway_text_node_set_background(struct sway_text_node *node, float background[4]); + +#endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 42fb71bf..f93d5d2b 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -1,5 +1,6 @@ #ifndef _SWAY_CONTAINER_H #define _SWAY_CONTAINER_H +#include #include #include #include @@ -68,11 +69,44 @@ struct sway_container { struct sway_node node; struct sway_view *view; + struct wlr_scene_tree *scene_tree; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *border; + struct wlr_scene_rect *background; + + struct sway_text_node *title_text; + struct sway_text_node *marks_text; + } title_bar; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *top; + struct wlr_scene_rect *bottom; + struct wlr_scene_rect *left; + struct wlr_scene_rect *right; + } border; + + struct wlr_scene_shadow *shadow; + + struct wlr_scene_tree *content_tree; + struct wlr_scene_rect *dim_rect; + struct wlr_scene_buffer *output_handler; + + struct wl_listener output_enter; + struct wl_listener output_leave; + struct sway_container_state current; struct sway_container_state pending; char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar + int title_width; + + char *title_format; enum sway_container_layout prev_split_layout; @@ -100,25 +134,11 @@ struct sway_container { double child_total_width; double child_total_height; - // In most cases this is the same as the content x and y, but if the view - // refuses to resize to the content dimensions then it can be smaller. - // These are in layout coordinates. - double surface_x, surface_y; - - // Outputs currently being intersected - list_t *outputs; // struct sway_output - // Indicates that the container is a scratchpad container. // Both hidden and visible scratchpad containers have scratchpad=true. // Hidden scratchpad containers have a NULL parent. bool scratchpad; - bool shadow_enabled; - - bool blur_enabled; - - float saturation; - // Stores last output size and position for adjusting coordinates of // scratchpad windows. // Unused for non-scratchpad windows. @@ -127,21 +147,11 @@ struct sway_container { float alpha; int corner_radius; - + bool blur_enabled; + bool shadow_enabled; float dim; - struct wlr_texture *title_focused; - struct wlr_texture *title_focused_inactive; - struct wlr_texture *title_focused_tab_title; - struct wlr_texture *title_unfocused; - struct wlr_texture *title_urgent; - list_t *marks; // char * - struct wlr_texture *marks_focused; - struct wlr_texture *marks_focused_inactive; - struct wlr_texture *marks_focused_tab_title; - struct wlr_texture *marks_unfocused; - struct wlr_texture *marks_urgent; struct { struct wl_signal destroy; @@ -161,19 +171,6 @@ void container_begin_destroy(struct sway_container *con); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); -/** - * Find a container at the given coordinates. Returns the surface and - * surface-local coordinates of the given layout coordinates if the container - * is a view and the view contains a surface at those coordinates. - */ -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy); - -struct sway_container *tiling_container_at( - struct sway_node *parent, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data); @@ -188,15 +185,15 @@ 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_damage_whole(struct sway_container *container); - void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); -void container_update_title_textures(struct sway_container *container); +void container_update_title_bar(struct sway_container *container); + +void container_update_marks(struct sway_container *container); + +size_t parse_title_format(struct sway_container *container, char *buffer); size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); @@ -232,11 +229,6 @@ void container_set_geometry_from_content(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); -/** - * Same as above, but for current container state. - */ -bool container_is_current_floating(struct sway_container *container); - /** * Get a container's box in layout coordinates. */ @@ -299,26 +291,12 @@ bool container_is_floating_or_child(struct sway_container *container); */ bool container_is_fullscreen_or_child(struct sway_container *container); -/** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. - * If the container is not on any output, return NULL. - */ -struct sway_output *container_get_effective_output(struct sway_container *con); - -void container_discover_outputs(struct sway_container *con); - enum sway_container_layout container_parent_layout(struct sway_container *con); -enum sway_container_layout container_current_parent_layout( - struct sway_container *con); - list_t *container_get_siblings(struct sway_container *container); int container_sibling_index(struct sway_container *child); -list_t *container_get_current_siblings(struct sway_container *container); - void container_handle_fullscreen_reparent(struct sway_container *con); void container_add_child(struct sway_container *parent, @@ -366,8 +344,6 @@ bool container_has_mark(struct sway_container *container, char *mark); void container_add_mark(struct sway_container *container, char *mark); -void container_update_marks_textures(struct sway_container *container); - void container_raise_floating(struct sway_container *con); bool container_is_scratchpad_hidden(struct sway_container *con); @@ -391,4 +367,14 @@ bool container_is_sticky_or_child(struct sway_container *con); */ int container_squash(struct sway_container *con); +void container_arrange_title_bar(struct sway_container *con); + +void container_update(struct sway_container *con); + +void container_update_itself_and_parents(struct sway_container *con); + +bool container_has_shadow(struct sway_container *con); + +bool container_has_corner_radius(struct sway_container *con); + #endif diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 03a389a4..e6106783 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,5 +1,6 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H +#include #include #include #include "list.h" @@ -75,4 +76,19 @@ list_t *node_get_children(struct sway_node *node); bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); +// when destroying a sway tree, it's not known which order the tree will be +// destroyed. To prevent freeing of scene_nodes recursing up the tree, +// let's use this helper function to disown them to the staging node. +void scene_node_disown_children(struct wlr_scene_tree *tree); + +// a helper function used to allocate tree nodes. If an allocation failure +// occurs a flag is flipped that can be checked later to destroy a parent +// of this scene node preventing memory leaks. +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed); + +struct wlr_scene_shadow *alloc_scene_shadow(struct wlr_scene_tree *parent, + int width, int height, int corner_radius, float blur_sigma, + const float color [static 4], bool *failed); + #endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index a2c088e7..b1b9e2ff 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -1,12 +1,13 @@ #ifndef _SWAY_ROOT_H #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; @@ -15,11 +16,44 @@ struct sway_root { struct sway_node node; struct wlr_output_layout *output_layout; - struct wl_listener output_layout_change; -#if HAVE_XWAYLAND - struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link + // scene node layout: + // - root + // - staging + // - layer shell stuff + // - tiling + // - floating + // - fullscreen stuff + // - seat stuff + // - ext_session_lock + struct wlr_scene *root_scene; + + // since wlr_scene nodes can't be orphaned and must always + // have a parent, use this staging scene_tree so that a + // node always have a valid parent. Nothing in this + // staging node will be visible. + struct wlr_scene_tree *staging; + + // tree containing all layers the compositor will render. Cursor handling + // will end up iterating this tree. + struct wlr_scene_tree *layer_tree; + + struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; + struct wlr_scene_tree *blur_tree; + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *floating; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *fullscreen_global; +#if WLR_HAS_XWAYLAND + struct wlr_scene_tree *unmanaged; #endif - struct wl_list drag_icons; // sway_drag_icon::link + struct wlr_scene_tree *shell_overlay; + struct wlr_scene_tree *popup; + struct wlr_scene_tree *seat; + struct wlr_scene_tree *session_lock; + } layers; // Includes disabled outputs struct wl_list all_outputs; // sway_output::link @@ -41,7 +75,7 @@ struct sway_root { } events; }; -struct sway_root *root_create(void); +struct sway_root *root_create(struct wl_display *display); void root_destroy(struct sway_root *root); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 76cfdf3a..bc2b128c 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -1,9 +1,12 @@ #ifndef _SWAY_VIEW_H #define _SWAY_VIEW_H +#include #include +#include #include +#include #include "sway/config.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #endif #include "sway/input/input-manager.h" @@ -14,7 +17,7 @@ struct sway_xdg_decoration; enum sway_view_type { SWAY_VIEW_XDG_SHELL, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND SWAY_VIEW_XWAYLAND, #endif }; @@ -26,12 +29,18 @@ 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 }; +enum sway_view_tearing_mode { + TEARING_OVERRIDE_FALSE, + TEARING_OVERRIDE_TRUE, + TEARING_WINDOW_HINT, +}; + struct sway_view_impl { void (*get_constraints)(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); @@ -45,10 +54,6 @@ struct sway_view_impl { void (*set_fullscreen)(struct sway_view *view, bool fullscreen); void (*set_resizing)(struct sway_view *view, bool resizing); bool (*wants_floating)(struct sway_view *view); - void (*for_each_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - void (*for_each_popup_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); bool (*is_transient_for)(struct sway_view *child, struct sway_view *ancestor); void (*close)(struct sway_view *view); @@ -56,19 +61,14 @@ struct sway_view_impl { void (*destroy)(struct sway_view *view); }; -struct sway_saved_buffer { - struct wlr_client_buffer *buffer; - int x, y; - int width, height; - enum wl_output_transform transform; - struct wlr_fbox source_box; - struct wl_list link; // sway_view::saved_buffers -}; - struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *content_tree; + struct wlr_scene_tree *saved_surface_tree; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; @@ -80,30 +80,24 @@ struct sway_view { // Used when changing a view from tiled to floating. int natural_width, natural_height; - char *title_format; - bool using_csd; struct timespec urgent; bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wl_list saved_buffers; // sway_saved_buffer::link - // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. struct wlr_box geometry; - // The "old" geometry during a transaction. Used to damage the old location - // when a transaction is applied. - struct wlr_box saved_geometry; + struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; struct wl_listener foreign_fullscreen_request; struct wl_listener foreign_close_request; - struct wl_listener foreign_destroy; struct wl_listener foreign_minimize; + struct wl_listener foreign_destroy; bool destroying; @@ -111,7 +105,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 }; @@ -120,11 +114,12 @@ struct sway_view { struct wl_signal unmap; } events; - struct wl_listener surface_new_subsurface; - int max_render_time; // In milliseconds enum seat_config_shortcuts_inhibit shortcuts_inhibit; + + enum sway_view_tearing_mode tearing_mode; + enum wp_tearing_control_v1_presentation_hint tearing_hint; }; struct sway_xdg_shell_view { @@ -143,10 +138,12 @@ 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; + struct wlr_scene_tree *surface_tree; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -168,18 +165,18 @@ struct sway_xwayland_view { struct wl_listener unmap; struct wl_listener destroy; struct wl_listener override_redirect; + + struct wl_listener surface_tree_destroy; }; struct sway_xwayland_unmanaged { struct wlr_xwayland_surface *wlr_xwayland_surface; - struct wl_list link; - int lx, ly; + struct wlr_scene_surface *surface_scene; struct wl_listener request_activate; struct wl_listener request_configure; struct wl_listener request_fullscreen; - struct wl_listener commit; struct wl_listener set_geometry; struct wl_listener associate; struct wl_listener dissociate; @@ -189,46 +186,24 @@ struct sway_xwayland_unmanaged { struct wl_listener override_redirect; }; #endif -struct sway_view_child; - -struct sway_view_child_impl { - void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy); - void (*destroy)(struct sway_view_child *child); -}; - -/** - * A view child is a surface in the view tree, such as a subsurface or a popup. - */ -struct sway_view_child { - const struct sway_view_child_impl *impl; - struct wl_list link; +struct sway_popup_desc { + struct wlr_scene_node *relative; struct sway_view *view; - struct sway_view_child *parent; - struct wl_list children; // sway_view_child::link - struct wlr_surface *surface; - bool mapped; - - struct wl_listener surface_commit; - struct wl_listener surface_new_subsurface; - struct wl_listener surface_map; - struct wl_listener surface_unmap; - struct wl_listener surface_destroy; - struct wl_listener view_unmap; -}; - -struct sway_subsurface { - struct sway_view_child child; - - struct wl_listener destroy; }; struct sway_xdg_popup { - struct sway_view_child child; + struct sway_view *view; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *xdg_surface_tree; struct wlr_xdg_popup *wlr_xdg_popup; + struct sway_popup_desc desc; + + struct wl_listener surface_commit; struct wl_listener new_popup; + struct wl_listener reposition; struct wl_listener destroy; }; @@ -301,23 +276,9 @@ void view_close(struct sway_view *view); void view_close_popups(struct sway_view *view); -void view_damage_from(struct sway_view *view); - -/** - * Iterate all surfaces of a view (toplevels + popups). - */ -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Iterate all popup surfaces of a view. - */ -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - // view implementation -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); void view_destroy(struct sway_view *view); @@ -339,23 +300,18 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view); void view_update_size(struct sway_view *view); -void view_center_surface(struct sway_view *view); - -void view_child_init(struct sway_view_child *child, - const struct sway_view_child_impl *impl, struct sway_view *view, - struct wlr_surface *surface); - -void view_child_destroy(struct sway_view_child *child); - +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 struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); +void view_update_app_id(struct sway_view *view); + /** * Re-read the view's title property and update any relevant title bars. * The force argument makes it recreate the title bars even if the title hasn't @@ -387,6 +343,8 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); -bool gaps_to_edge(struct sway_view *view); +void view_send_frame_done(struct sway_view *view); + +bool view_can_tear(struct sway_view *view); #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 1e7d9b82..cdd92bc2 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -1,6 +1,7 @@ #ifndef _SWAY_WORKSPACE_H #define _SWAY_WORKSPACE_H +#include #include #include "sway/config.h" #include "sway/tree/container.h" @@ -23,6 +24,12 @@ struct sway_workspace_state { struct sway_workspace { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + struct sway_container *fullscreen; char *name; @@ -93,9 +100,6 @@ struct sway_output *workspace_output_get_highest_available( void workspace_detect_urgent(struct sway_workspace *workspace); -bool workspace_get_blur_info(struct sway_workspace *ws, - pixman_region32_t *blur_region); - void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data); diff --git a/include/sway/xdg_decoration.h b/include/sway/xdg_decoration.h index 8bef4c6d..2388ebcb 100644 --- a/include/sway/xdg_decoration.h +++ b/include/sway/xdg_decoration.h @@ -16,4 +16,6 @@ struct sway_xdg_decoration { struct sway_xdg_decoration *xdg_decoration_from_surface( struct wlr_surface *surface); +void set_xdg_decoration_mode(struct sway_xdg_decoration *deco); + #endif diff --git a/include/swaybar/image.h b/include/swaybar/image.h new file mode 100644 index 00000000..53a210dd --- /dev/null +++ b/include/swaybar/image.h @@ -0,0 +1,7 @@ +#ifndef _SWAYBAR_IMAGE_H +#define _SWAYBAR_IMAGE_H +#include + +cairo_surface_t *load_image(const char *path); + +#endif diff --git a/meson.build b/meson.build index d5086804..9641b7ef 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '0.4', + version: '1.10', license: 'MIT', meson_version: '>=0.60.0', default_options: [ @@ -11,9 +11,12 @@ project( ], ) +original_version = '1.10.0' + add_project_arguments( [ '-DWLR_USE_UNSTABLE', + '-D_POSIX_C_SOURCE=200809L', '-Wno-unused-parameter', '-Wno-unused-result', @@ -41,17 +44,17 @@ subproject( 'scenefx', required: false, ) -scenefx = dependency('scenefx') +scenefx = dependency('scenefx-0.2', fallback: 'scenefx') # Execute the wlroots subproject, if any -wlroots_version = ['>=0.17.0', '<0.18.0'] +wlroots_version = ['>=0.18.0', '<0.19.0'] subproject( 'wlroots', default_options: ['examples=false'], 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, @@ -63,10 +66,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') @@ -74,9 +73,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_egl = dependency('wayland-egl') -egl = dependency('egl') -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') @@ -84,18 +81,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')) -drm_full = dependency('libdrm') # only needed for drm_fourcc.h -drm = drm_full.partial_dependency(compile_args: true, includes: true) +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') @@ -119,17 +113,14 @@ 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') conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') conf_data.set10('HAVE_TRAY', have_tray) -conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol( - 'libinput.h', - 'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', - dependencies: libinput, -)) +foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY'] + conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) +endforeach scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() @@ -176,8 +167,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir version = '"@0@"'.format(meson.project_version()) git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) - git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) + git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) + git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) if git_commit.returncode() == 0 and git_branch.returncode() == 0 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( meson.project_version(), @@ -187,7 +178,8 @@ if git.found() endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -add_project_arguments('-DSWAY_ORIGINAL_VERSION="1.9.0"', language: 'c') +add_project_arguments('-DSWAY_ORIGINAL_VERSION="@0@"'.format(original_version), language: 'c') +add_project_arguments('-DSCENEFX_VERSION="@0@"'.format(scenefx.version()), language: 'c') # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') @@ -281,7 +273,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 2992ac58..d96f8757 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -7,16 +7,16 @@ 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', + wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', - 'wlr-input-inhibitor-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml', ] diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml deleted file mode 100644 index b62d1bb4..00000000 --- a/protocols/wlr-input-inhibitor-unstable-v1.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - 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. - - - - - 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. - - - - - Activates the input inhibitor. As long as the inhibitor is active, the - compositor will not send input events 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). - - - - - Destroy the inhibitor and allow other clients to receive input. - - - - diff --git a/sway-portals.conf b/sway-portals.conf deleted file mode 100644 index aa046f63..00000000 --- a/sway-portals.conf +++ /dev/null @@ -1,6 +0,0 @@ -[preferred] -# Use xdg-desktop-portal-gtk for every portal interface... -default=gtk -# ... except for the ScreenCast and Screenshot -org.freedesktop.impl.portal.ScreenCast=wlr -org.freedesktop.impl.portal.Screenshot=wlr diff --git a/sway.desktop b/sway.desktop index 9f33bcff..420db5aa 100644 --- a/sway.desktop +++ b/sway.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Name=SwayFX +Name=Sway Comment=An i3-compatible Wayland compositor Exec=sway Type=Application diff --git a/sway/commands.c b/sway/commands.c index 2e048203..6a8c5a0b 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809 #include #include #include @@ -50,7 +49,7 @@ static const struct cmd_handler handlers[] = { { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "blur", cmd_blur }, - { "blur_brightness", cmd_blur_brightness }, + { "blur_brightness", cmd_blur_brightness }, { "blur_contrast", cmd_blur_contrast }, { "blur_noise", cmd_blur_noise }, { "blur_passes", cmd_blur_passes }, @@ -126,8 +125,8 @@ static const struct cmd_handler handlers[] = { static const struct cmd_handler config_handlers[] = { { "default_orientation", cmd_default_orientation }, { "include", cmd_include }, - { "scratchpad_minimize", cmd_scratchpad_minimize }, { "primary_selection", cmd_primary_selection }, + { "scratchpad_minimize", cmd_scratchpad_minimize }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, { "workspace_layout", cmd_workspace_layout }, @@ -136,6 +135,7 @@ static const struct cmd_handler config_handlers[] = { /* Runtime-only commands. Keep alphabetized */ static const struct cmd_handler command_handlers[] = { + { "allow_tearing", cmd_allow_tearing }, { "border", cmd_border }, { "create_output", cmd_create_output }, { "exit", cmd_exit }, @@ -152,7 +152,6 @@ static const struct cmd_handler command_handlers[] = { { "reload", cmd_reload }, { "rename", cmd_rename }, { "resize", cmd_resize }, - { "saturation", cmd_saturation }, { "scratchpad", cmd_scratchpad }, { "shortcuts_inhibitor", cmd_shortcuts_inhibitor }, { "split", cmd_split }, diff --git a/sway/commands/allow_tearing.c b/sway/commands/allow_tearing.c new file mode 100644 index 00000000..ee594138 --- /dev/null +++ b/sway/commands/allow_tearing.c @@ -0,0 +1,24 @@ +#include +#include "sway/config.h" +#include "sway/tree/view.h" +#include "util.h" + +struct cmd_results *cmd_allow_tearing(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) { + return error; + } + + struct sway_container *container = config->handler_context.container; + if (!container || !container->view) { + return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views"); + } + + bool wants_tearing = parse_boolean(argv[0], true); + + struct sway_view *view = container->view; + view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE : + TEARING_OVERRIDE_FALSE; + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/assign.c b/sway/commands/assign.c index f7d911f7..bf95cf00 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 22756acb..635e895b 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809 #include #include #include diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 891c87af..0c074679 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 8b661e3a..7b38831e 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index 6ac07843..fee21709 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "sway/commands.h" diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index a9a61743..46cf4ca9 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 7c2f423b..d69e910b 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index cac1d056..51730176 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index b207de0b..94f530ec 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 6737d4d2..50e9a873 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index eb3b486e..679facf7 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "sway/commands.h" diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 979e178f..268f2855 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/blur.c b/sway/commands/blur.c index 52e3fdb9..366f030b 100644 --- a/sway/commands/blur.c +++ b/sway/commands/blur.c @@ -1,9 +1,14 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" +#include "sway/tree/arrange.h" +#include "sway/tree/root.h" #include "util.h" +static void arrange_blur_iter(struct sway_container *con, void *data) { + con->blur_enabled = config->blur_enabled; +} + struct cmd_results *cmd_blur(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "blur", EXPECTED_AT_LEAST, 1); @@ -16,16 +21,18 @@ struct cmd_results *cmd_blur(int argc, char **argv) { bool result = parse_boolean(argv[0], true); if (con == NULL) { config->blur_enabled = result; + + // Config reload: reset all containers to config value + root_for_each_container(arrange_blur_iter, NULL); + arrange_root(); } else { con->blur_enabled = result; + container_update(con); } struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); + wlr_scene_optimized_blur_mark_dirty(output->layers.blur_layer); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_brightness.c b/sway/commands/blur_brightness.c index 6ff60975..123fb899 100644 --- a/sway/commands/blur_brightness.c +++ b/sway/commands/blur_brightness.c @@ -1,7 +1,5 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" +#include "sway/tree/root.h" struct cmd_results *cmd_blur_brightness(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,15 +13,10 @@ struct cmd_results *cmd_blur_brightness(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid brightness specified"); } - config->blur_params.brightness = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.brightness = value; + wlr_scene_set_blur_data(root_scene, blur_data); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_contrast.c b/sway/commands/blur_contrast.c index ba046e63..74cc6f79 100644 --- a/sway/commands/blur_contrast.c +++ b/sway/commands/blur_contrast.c @@ -1,7 +1,4 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" struct cmd_results *cmd_blur_contrast(int argc, char **argv) { struct cmd_results *error = NULL; @@ -12,18 +9,13 @@ struct cmd_results *cmd_blur_contrast(int argc, char **argv) { char *inv; float value = strtof(argv[0], &inv); if (*inv != '\0' || value < 0 || value > 2) { - return cmd_results_new(CMD_FAILURE, "Invalid brightness specified"); + return cmd_results_new(CMD_FAILURE, "Invalid contrast specified"); } - config->blur_params.contrast = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.contrast = value; + wlr_scene_set_blur_data(root_scene, blur_data); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_noise.c b/sway/commands/blur_noise.c index 358b20f5..4b99d73e 100644 --- a/sway/commands/blur_noise.c +++ b/sway/commands/blur_noise.c @@ -4,26 +4,21 @@ #include "sway/output.h" struct cmd_results *cmd_blur_noise(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "blur_noise", EXPECTED_EQUAL_TO, 1))) { - return error; - } - - char *inv; - float value = strtof(argv[0], &inv); - if (*inv != '\0' || value < 0 || value > 1) { - return cmd_results_new(CMD_FAILURE, "Invalid noise specified"); - } - - config->blur_params.noise = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "blur_noise", EXPECTED_EQUAL_TO, 1))) { + return error; } - return cmd_results_new(CMD_SUCCESS, NULL); + char *inv; + float value = strtof(argv[0], &inv); + if (*inv != '\0' || value < 0 || value > 1) { + return cmd_results_new(CMD_FAILURE, "Invalid noise specified"); + } + + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.noise = value; + wlr_scene_set_blur_data(root_scene, blur_data); + + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_passes.c b/sway/commands/blur_passes.c index c79f99d1..60a06794 100644 --- a/sway/commands/blur_passes.c +++ b/sway/commands/blur_passes.c @@ -1,7 +1,4 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" struct cmd_results *cmd_blur_passes(int argc, char **argv) { struct cmd_results *error = NULL; @@ -12,18 +9,13 @@ struct cmd_results *cmd_blur_passes(int argc, char **argv) { char *inv; int value = strtol(argv[0], &inv, 10); if (*inv != '\0' || value < 0 || value > 10) { - return cmd_results_new(CMD_FAILURE, "Invalid size specified"); + return cmd_results_new(CMD_FAILURE, "Invalid number of passes specified"); } - config->blur_params.num_passes = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.num_passes = value; + wlr_scene_set_blur_data(root_scene, blur_data); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_radius.c b/sway/commands/blur_radius.c index 85e90e77..cb447c9b 100644 --- a/sway/commands/blur_radius.c +++ b/sway/commands/blur_radius.c @@ -1,7 +1,4 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" struct cmd_results *cmd_blur_radius(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,15 +12,10 @@ struct cmd_results *cmd_blur_radius(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid size specified"); } - config->blur_params.radius = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.radius = value; + wlr_scene_set_blur_data(root_scene, blur_data); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_saturation.c b/sway/commands/blur_saturation.c index ae6352d2..6e2892d1 100644 --- a/sway/commands/blur_saturation.c +++ b/sway/commands/blur_saturation.c @@ -1,7 +1,4 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" struct cmd_results *cmd_blur_saturation(int argc, char **argv) { struct cmd_results *error = NULL; @@ -15,16 +12,10 @@ struct cmd_results *cmd_blur_saturation(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid saturation specified"); } - config->blur_params.saturation = value; - - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct wlr_scene *root_scene = root->root_scene; + struct blur_data blur_data = root_scene->blur_data; + blur_data.saturation = value; + wlr_scene_set_blur_data(root_scene, blur_data); return cmd_results_new(CMD_SUCCESS, NULL); } - diff --git a/sway/commands/blur_xray.c b/sway/commands/blur_xray.c index 6986b361..b5737fd3 100644 --- a/sway/commands/blur_xray.c +++ b/sway/commands/blur_xray.c @@ -1,7 +1,7 @@ -#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" +#include "sway/tree/root.h" #include "util.h" struct cmd_results *cmd_blur_xray(int argc, char **argv) { @@ -16,10 +16,7 @@ struct cmd_results *cmd_blur_xray(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - output_damage_whole(output); + wlr_scene_optimized_blur_mark_dirty(output->layers.blur_layer); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/client.c b/sway/commands/client.c index 77263145..fd2ac7a8 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -5,9 +5,8 @@ #include "sway/tree/container.h" #include "util.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void container_update_iterator(struct sway_container *con, void *data) { + container_update(con); } static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, @@ -51,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, memcpy(class, &colors, sizeof(struct border_colors)); if (config->active) { - root_for_each_container(rebuild_textures_iterator, NULL); - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + root_for_each_container(container_update_iterator, NULL); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/corner_radius.c b/sway/commands/corner_radius.c index fe8b458f..0129954e 100644 --- a/sway/commands/corner_radius.c +++ b/sway/commands/corner_radius.c @@ -1,8 +1,10 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" -#include "log.h" +#include "sway/tree/workspace.h" bool cmd_corner_radius_parse_value(char *arg, int* result) { char *inv; @@ -14,6 +16,11 @@ bool cmd_corner_radius_parse_value(char *arg, int* result) { return true; } +static void arrange_corner_radius_iter(struct sway_container *con, void *data) { + con->corner_radius = config->corner_radius; +} + +// TODO: handle setting per container struct cmd_results *cmd_corner_radius(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "corner_radius", EXPECTED_EQUAL_TO, 1))) { @@ -27,6 +34,12 @@ struct cmd_results *cmd_corner_radius(int argc, char **argv) { config->corner_radius = value; + if (!config->handler_context.container) { + // Config reload: reset all containers to config value + root_for_each_container(arrange_corner_radius_iter, NULL); + arrange_root(); + } + /* titlebar padding depends on corner_radius to ensure that titlebars are rendered nicely diff --git a/sway/commands/default_dim_inactive.c b/sway/commands/default_dim_inactive.c index 04d8acdf..b6a681bb 100644 --- a/sway/commands/default_dim_inactive.c +++ b/sway/commands/default_dim_inactive.c @@ -1,9 +1,7 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "log.h" -#include "sway/output.h" - +#include "sway/tree/arrange.h" struct cmd_results *cmd_default_dim_inactive(int argc, char **argv) { struct cmd_results *error = NULL; @@ -20,10 +18,7 @@ struct cmd_results *cmd_default_dim_inactive(int argc, char **argv) { config->default_dim_inactive = val; if (config->active) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + arrange_root(); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/dim_inactive.c b/sway/commands/dim_inactive.c index e287ba4d..fd633d98 100644 --- a/sway/commands/dim_inactive.c +++ b/sway/commands/dim_inactive.c @@ -1,7 +1,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "log.h" #include "sway/output.h" #include "sway/tree/container.h" @@ -18,10 +17,10 @@ struct cmd_results *cmd_dim_inactive(int argc, char **argv) { } struct sway_container *container = config->handler_context.container; - if (!container) { - return cmd_results_new(CMD_INVALID, "cmd_dim cannot be used without a for_window rule"); - } + if (!container) { + return cmd_results_new(CMD_INVALID, "cmd_dim cannot be used without a for_window rule"); + } - container->dim = val; + container->dim = val; return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/dim_inactive_colors.c b/sway/commands/dim_inactive_colors.c index db8cc299..c5eab89a 100644 --- a/sway/commands/dim_inactive_colors.c +++ b/sway/commands/dim_inactive_colors.c @@ -1,8 +1,6 @@ -#include "log.h" #include "sway/commands.h" #include "sway/config.h" -#include "sway/output.h" -#include "sway/tree/container.h" +#include "sway/tree/arrange.h" #include "util.h" static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, @@ -20,10 +18,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, color_to_rgba(config_option, color); if (config->active) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + arrange_root(); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8fca1909..8bc1048c 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/font.c b/sway/commands/font.c index 74bb6b9f..9920d03e 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 1deeb56e..7ac6fcff 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -215,15 +215,13 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return error; } - bool config_loading = !config->active || config->reloading; - if (argc == 2) { return gaps_set_defaults(argc, argv); } - if (argc == 4 && !config_loading) { + if (argc == 4 && !config->reading) { return gaps_set_runtime(argc, argv); } - if (config_loading) { + if (config->reading) { return cmd_results_new(CMD_INVALID, "Expected %s", expected_defaults); } return cmd_results_new(CMD_INVALID, "Expected %s or %s", diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index d4442cc3..90a20716 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "gesture.h" diff --git a/sway/commands/input.c b/sway/commands/input.c index 306c40f7..310375a9 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 }, @@ -93,7 +94,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { return res; } - if (!config->reloading) { + if (!config->reading) { input_manager_apply_input_config(ic); } } else { diff --git a/sway/commands/input/calibration_matrix.c b/sway/commands/input/calibration_matrix.c index 38749fbb..53fe2c35 100644 --- a/sway/commands/input/calibration_matrix.c +++ b/sway/commands/input/calibration_matrix.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/config.h" 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/map_from_region.c b/sway/commands/input/map_from_region.c index 4400e111..2f8f753d 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/input/map_to_output.c b/sway/commands/input/map_to_output.c index f60fb7d5..a7266baa 100644 --- a/sway/commands/input/map_to_output.c +++ b/sway/commands/input/map_to_output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/config.h" diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index ad535db2..9087c589 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c index 493f94fb..056f00e5 100644 --- a/sway/commands/input/xkb_file.c +++ b/sway/commands/input/xkb_file.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/config.h" diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c index 22626517..1d01886c 100644 --- a/sway/commands/input/xkb_layout.c +++ b/sway/commands/input/xkb_layout.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c index f4a33de3..a9144a8a 100644 --- a/sway/commands/input/xkb_model.c +++ b/sway/commands/input/xkb_model.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c index 87d3e60c..bbe848fe 100644 --- a/sway/commands/input/xkb_numlock.c +++ b/sway/commands/input/xkb_numlock.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "util.h" diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c index d609293f..7ca20777 100644 --- a/sway/commands/input/xkb_options.c +++ b/sway/commands/input/xkb_options.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c index 3b59622c..8fbd26fb 100644 --- a/sway/commands/input/xkb_rules.c +++ b/sway/commands/input/xkb_rules.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index 3cce4ec8..623dfa18 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -1,9 +1,9 @@ -#define _POSIX_C_SOURCE 200809L #include #include #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 { @@ -95,10 +95,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { continue; } + struct wlr_keyboard *keyboard = + wlr_keyboard_from_input_device(dev->wlr_device); + if (keyboard->keymap == NULL && dev->is_virtual) { + // The `sway_keyboard_set_layout` function is by default skipped + // when configuring virtual keyboards. + continue; + } + struct xkb_switch_layout_action *action = &actions[actions_len++]; + action->keyboard = keyboard; - action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); if (relative) { action->layout = get_layout_relative(action->keyboard, relative); } else { diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c index d0e21d77..2d14ea9c 100644 --- a/sway/commands/input/xkb_variant.c +++ b/sway/commands/input/xkb_variant.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/config.h" #include "sway/commands.h" #include "log.h" diff --git a/sway/commands/layer_effects.c b/sway/commands/layer_effects.c index 3d5cc8c0..aea815d3 100644 --- a/sway/commands/layer_effects.c +++ b/sway/commands/layer_effects.c @@ -1,11 +1,11 @@ -#include +#include #include "log.h" #include "stringop.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/layer_criteria.h" +#include "sway/layers.h" #include "sway/output.h" -#include "util.h" +#include "sway/scene_descriptor.h" +#include "sway/tree/arrange.h" struct cmd_results *cmd_layer_effects(int argc, char **argv) { struct cmd_results *error = NULL; @@ -13,21 +13,38 @@ struct cmd_results *cmd_layer_effects(int argc, char **argv) { return error; } - struct layer_criteria *criteria = malloc(sizeof(struct layer_criteria)); - criteria->namespace = malloc(strlen(argv[0]) + 1); - strcpy(criteria->namespace, argv[0]); - criteria->cmdlist = join_args(argv + 1, argc - 1); - - // Check if the rule already exists - if (layer_criteria_already_exists(criteria)) { - sway_log(SWAY_DEBUG, "layer_effect already exists: '%s' '%s'", + struct layer_criteria *criteria = layer_criteria_add(argv[0], join_args(argv + 1, argc - 1)); + if (criteria) { + sway_log(SWAY_DEBUG, "layer_effect: '%s' '%s' added", criteria->namespace, criteria->cmdlist); - layer_criteria_destroy(criteria); - return cmd_results_new(CMD_SUCCESS, NULL); - } - list_add(config->layer_criteria, criteria); - sway_log(SWAY_DEBUG, "layer_effect: '%s' '%s' added", criteria->namespace, criteria->cmdlist); + // Apply the criteria to all applicable layer surfaces + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + struct wlr_scene_tree *layers[] = { + output->layers.shell_background, + output->layers.shell_bottom, + output->layers.shell_overlay, + output->layers.shell_top, + }; + size_t nlayers = sizeof(layers) / sizeof(layers[0]); + struct wlr_scene_node *node; + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(node, &layers[i]->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (!surface) { + continue; + } + if (strcmp(surface->layer_surface->namespace, criteria->namespace) == 0) { + layer_apply_criteria(surface, criteria); + } + } + } + + arrange_layers(output); + } + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/mark.c b/sway/commands/mark.c index aa5f185c..2bfc86b3 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -59,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } free(mark); - container_update_marks_textures(container); + container_update_marks(container); if (container->view) { view_execute_criteria(container->view); } diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 7263efcb..b3216967 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" diff --git a/sway/commands/move.c b/sway/commands/move.c index 69ed06c0..8891514c 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -12,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" @@ -240,7 +240,6 @@ static void container_move_to_workspace(struct sway_container *container, static void container_move_to_container(struct sway_container *container, struct sway_container *destination) { if (container == destination - || container_has_ancestor(container, destination) || container_has_ancestor(destination, container)) { return; } @@ -510,6 +509,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } } ws = workspace_create(NULL, ws_name); + arrange_workspace(ws); } free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); @@ -770,15 +770,6 @@ static struct cmd_results *cmd_move_in_direction( ipc_event_window(container, "move"); } - // Hack to re-focus container - seat_set_raw_focus(config->handler_context.seat, &new_ws->node); - seat_set_focus_container(config->handler_context.seat, container); - - if (old_ws != new_ws) { - ipc_event_workspace(old_ws, new_ws, "focus"); - workspace_detect_urgent(old_ws); - workspace_detect_urgent(new_ws); - } container_end_mouse_operation(container); return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228e..610cecc6 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; - container_damage_whole(con); + container_update(con); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/output.c b/sway/commands/output.c index 462dffd2..9d58413f 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -8,8 +8,10 @@ // must be in order for the bsearch static const struct cmd_handler output_handlers[] = { { "adaptive_sync", output_cmd_adaptive_sync }, + { "allow_tearing", output_cmd_allow_tearing }, { "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 }, @@ -103,19 +105,18 @@ struct cmd_results *cmd_output(int argc, char **argv) { bool background = output->background; - output = store_output_config(output); + store_output_config(output); - // If reloading, the output configs will be applied after reading the - // entire config and before the deferred commands so that an auto generated - // workspace name is not given to re-enabled outputs. - if (!config->reloading && !config->validating) { - apply_output_config_to_outputs(output); - if (background) { - if (!spawn_swaybg()) { - return cmd_results_new(CMD_FAILURE, - "Failed to apply background configuration"); - } - } + if (config->reading) { + // When reading the config file, we wait till the end to do a single + // modeset and swaybg spawn. + return cmd_results_new(CMD_SUCCESS, NULL); + } + request_modeset(); + + if (background && !spawn_swaybg()) { + return cmd_results_new(CMD_FAILURE, + "Failed to apply background configuration"); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/output/adaptive_sync.c b/sway/commands/output/adaptive_sync.c index 7382e2ee..4ce88f88 100644 --- a/sway/commands/output/adaptive_sync.c +++ b/sway/commands/output/adaptive_sync.c @@ -1,5 +1,7 @@ +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/output.h" #include "util.h" struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { @@ -10,12 +12,26 @@ struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing adaptive_sync argument"); } - if (parse_boolean(argv[0], true)) { - config->handler_context.output_config->adaptive_sync = 1; - } else { - config->handler_context.output_config->adaptive_sync = 0; + bool current_value = true; + if (strcasecmp(argv[0], "toggle") == 0) { + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, + "Cannot apply toggle to all outputs"); + } + + struct sway_output *sway_output = all_output_by_name_or_id(oc_name); + if (!sway_output || !sway_output->wlr_output) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc_name); + } + + current_value = + sway_output->wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; } + config->handler_context.output_config->adaptive_sync = parse_boolean(argv[0], current_value); + config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; return NULL; diff --git a/sway/commands/output/allow_tearing.c b/sway/commands/output/allow_tearing.c new file mode 100644 index 00000000..9a183b96 --- /dev/null +++ b/sway/commands/output/allow_tearing.c @@ -0,0 +1,23 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "util.h" + +struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (argc == 0) { + return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument"); + } + + if (parse_boolean(argv[0], + (config->handler_context.output_config->allow_tearing == 1))) { + config->handler_context.output_config->allow_tearing = 1; + } else { + config->handler_context.output_config->allow_tearing = 0; + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index d691295f..55bd7671 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c new file mode 100644 index 00000000..98481329 --- /dev/null +++ b/sway/commands/output/color_profile.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "stringop.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"); + } + + char *icc_path = strdup(argv[1]); + if (!expand_path(&icc_path)) { + struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, + "Invalid color profile specification: invalid file path"); + free(icc_path); + return cmd_res; + } + + void *data = NULL; + size_t size = 0; + if (!read_file_into_buf(icc_path, &data, &size)) { + free(icc_path); + return cmd_results_new(CMD_FAILURE, + "Failed to load color profile: could not read ICC file"); + } + free(icc_path); + + 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/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c index c419321e..3fa30e04 100644 --- a/sway/commands/output/render_bit_depth.c +++ b/sway/commands/output/render_bit_depth.c @@ -11,7 +11,10 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); } - if (strcmp(*argv, "8") == 0) { + if (strcmp(*argv, "6") == 0) { + config->handler_context.output_config->render_bit_depth = + RENDER_BIT_DEPTH_6; + } else if (strcmp(*argv, "8") == 0) { config->handler_context.output_config->render_bit_depth = RENDER_BIT_DEPTH_8; } else if (strcmp(*argv, "10") == 0) { @@ -19,7 +22,7 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { RENDER_BIT_DEPTH_10; } else { return cmd_results_new(CMD_INVALID, - "Invalid bit depth. Must be a value in (8|10)."); + "Invalid bit depth. Must be a value in (6|8|10)."); } config->handler_context.leftovers.argc = argc - 1; diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c index 6342d526..c6b72845 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c @@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { config->handler_context.output_config->enabled = 1; } - free(oc); + free_output_config(oc); config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index f4fcc8c9..8db71bb3 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c @@ -1,4 +1,5 @@ #include +#include #include "sway/commands.h" #include "sway/config.h" #include "log.h" diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c index 585b079d..9e2689c2 100644 --- a/sway/commands/primary_selection.c +++ b/sway/commands/primary_selection.c @@ -12,12 +12,14 @@ struct cmd_results *cmd_primary_selection(int argc, char **argv) { bool primary_selection = parse_boolean(argv[0], true); + // config->primary_selection is reset to the previous value on reload in + // load_main_config() if (config->reloading && config->primary_selection != primary_selection) { return cmd_results_new(CMD_FAILURE, "primary_selection can only be enabled/disabled at launch"); } - config->primary_selection = parse_boolean(argv[0], true); + config->primary_selection = primary_selection; return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 76f14bba..6c0aac26 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -9,9 +8,8 @@ #include "list.h" #include "log.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_title_bar(con); } static void do_reload(void *data) { @@ -48,7 +46,7 @@ static void do_reload(void *data) { } list_free_items_and_destroy(bar_ids); - root_for_each_container(rebuild_textures_iterator, NULL); + root_for_each_container(title_bar_update_iterator, NULL); arrange_root(); } diff --git a/sway/commands/saturation.c b/sway/commands/saturation.c deleted file mode 100644 index 35f02128..00000000 --- a/sway/commands/saturation.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include -#include -#include "sway/commands.h" -#include "sway/tree/view.h" -#include "log.h" - -struct cmd_results *cmd_saturation(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "saturation", EXPECTED_AT_LEAST, 1))) { - return error; - } - - struct sway_container *con = config->handler_context.container; - - if (con == NULL) { - return cmd_results_new(CMD_FAILURE, "No current container"); - } - - char *err; - float val = strtof(argc == 1 ? argv[0] : argv[1], &err); - if (*err) { - return cmd_results_new(CMD_INVALID, "saturation float invalid"); - } - - if (!strcasecmp(argv[0], "plus")) { - val = con->saturation + val; - } else if (!strcasecmp(argv[0], "minus")) { - val = con->saturation - val; - } else if (argc > 1 && strcasecmp(argv[0], "set")) { - return cmd_results_new(CMD_INVALID, - "Expected: set|plus|minus <0..2>: %s", argv[0]); - } - - if (val < 0 || val > 2) { - return cmd_results_new(CMD_FAILURE, "saturation value out of bounds"); - } - - con->saturation = val; - container_damage_whole(con); - return cmd_results_new(CMD_SUCCESS, NULL); -} diff --git a/sway/commands/scratchpad_minimize.c b/sway/commands/scratchpad_minimize.c index 1245e1d5..03c35b79 100644 --- a/sway/commands/scratchpad_minimize.c +++ b/sway/commands/scratchpad_minimize.c @@ -1,8 +1,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "log.h" -#include "stringop.h" #include "util.h" struct cmd_results *cmd_scratchpad_minimize(int argc, char **argv) { diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 00bfdab6..47d18546 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 5a8a3bc8..434e6bbb 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -6,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); @@ -85,12 +85,12 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { static struct cmd_results *press_or_release(struct sway_cursor *cursor, char *action, char *button_str) { - enum wlr_button_state state; + enum wl_pointer_button_state state; uint32_t button; if (strcasecmp(action, "press") == 0) { - state = WLR_BUTTON_PRESSED; + state = WL_POINTER_BUTTON_STATE_PRESSED; } else if (strcasecmp(action, "release") == 0) { - state = WLR_BUTTON_RELEASED; + state = WL_POINTER_BUTTON_STATE_RELEASED; } else { return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } @@ -105,16 +105,16 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN || button == SWAY_SCROLL_LEFT || button == SWAY_SCROLL_RIGHT) { // Dispatch axis event - enum wlr_axis_orientation orientation = + enum wl_pointer_axis orientation = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN) - ? WLR_AXIS_ORIENTATION_VERTICAL - : WLR_AXIS_ORIENTATION_HORIZONTAL; + ? WL_POINTER_AXIS_VERTICAL_SCROLL + : WL_POINTER_AXIS_HORIZONTAL_SCROLL; double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) ? -1 : 1; struct wlr_pointer_axis_event event = { .pointer = NULL, .time_msec = 0, - .source = WLR_AXIS_SOURCE_WHEEL, + .source = WL_POINTER_AXIS_SOURCE_WHEEL, .orientation = orientation, .delta = delta * 15, .delta_discrete = delta diff --git a/sway/commands/seat/hide_cursor.c b/sway/commands/seat/hide_cursor.c index e09b82d9..f5177a47 100644 --- a/sway/commands/seat/hide_cursor.c +++ b/sway/commands/seat/hide_cursor.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 62b94db2..2974453e 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include 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/seat/xcursor_theme.c b/sway/commands/seat/xcursor_theme.c index 202f35b9..61322a57 100644 --- a/sway/commands/seat/xcursor_theme.c +++ b/sway/commands/seat/xcursor_theme.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" diff --git a/sway/commands/set.c b/sway/commands/set.c index c539e9fc..ba384c7c 100644 --- a/sway/commands/set.c +++ b/sway/commands/set.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/shadows.c b/sway/commands/shadows.c index a213de8f..a7425b3d 100644 --- a/sway/commands/shadows.c +++ b/sway/commands/shadows.c @@ -6,6 +6,10 @@ #include "sway/tree/container.h" #include "util.h" +static void arrange_shadow_iter(struct sway_container *con, void *data) { + con->shadow_enabled = config->shadow_enabled; +} + struct cmd_results *cmd_shadows(int argc, char **argv) { struct cmd_results *error = checkarg(argc, "shadows", EXPECTED_AT_LEAST, 1); @@ -18,9 +22,10 @@ struct cmd_results *cmd_shadows(int argc, char **argv) { bool result = parse_boolean(argv[0], true); if (con == NULL) { config->shadow_enabled = result; + // Config reload: reset all containers to config value + root_for_each_container(arrange_shadow_iter, NULL); } else { con->shadow_enabled = result; - container_damage_whole(con); } arrange_root(); 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/show_marks.c b/sway/commands/show_marks.c index 0d373b80..60cef9fa 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -10,8 +9,8 @@ #include "stringop.h" #include "util.h" -static void rebuild_marks_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_marks(con); } struct cmd_results *cmd_show_marks(int argc, char **argv) { @@ -23,12 +22,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { - root_for_each_container(rebuild_marks_iterator, NULL); - } - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); + root_for_each_container(title_bar_update_iterator, NULL); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index d44eb006..c0b0d0b9 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "config.h" #include "log.h" @@ -19,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 @@ -54,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/title_align.c b/sway/commands/title_align.c index c30355de..be298a29 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c @@ -4,6 +4,10 @@ #include "sway/tree/container.h" #include "sway/tree/root.h" +static void arrange_title_bar_iterator(struct sway_container *con, void *data) { + container_arrange_title_bar(con); +} + struct cmd_results *cmd_title_align(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { @@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { "Expected 'title_align left|center|right'"); } - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + root_for_each_container(arrange_title_bar_iterator, NULL); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index a2446b7e..fbade473 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -12,16 +12,19 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { return error; } struct sway_container *container = config->handler_context.container; - if (!container || !container->view) { + if (!container) { return cmd_results_new(CMD_INVALID, - "Only views can have a title_format"); + "Only valid containers can have a title_format"); } - struct sway_view *view = container->view; char *format = join_args(argv, argc); - if (view->title_format) { - free(view->title_format); + if (container->title_format) { + free(container->title_format); + } + container->title_format = format; + if (container->view) { + view_update_title(container->view, true); + } else { + container_update_representation(container); } - view->title_format = format; - view_update_title(view, true); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163..fa3db3c5 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { "Expected output to have a workspace"); } arrange_workspace(ws); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index affb6b50..6999f7a2 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c @@ -2,7 +2,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" -#include "sway/swaynag.h" #include "sway/tree/arrange.h" #include "log.h" @@ -28,33 +27,12 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { } } - /* - titlebar padding depends on corner_radius to - ensure that titlebars are rendered nicely - */ - if (v_value > (config->corner_radius - config->font_height) / 2) { - config->titlebar_v_padding = v_value; - } else { - config_add_swaynag_warning( - "titlebar_v_padding (%d) is too small for the current corner radius (%d)", - v_value, config->corner_radius - ); - return cmd_results_new(CMD_FAILURE, NULL); - } - if (h_value > config->corner_radius) { - config->titlebar_h_padding = h_value; - } else { - config_add_swaynag_warning( - "titlebar_h_padding (%d) is too small for the current corner radius (%d)", - h_value, config->corner_radius - ); - return cmd_results_new(CMD_FAILURE, NULL); - } + config->titlebar_v_padding = v_value; + config->titlebar_h_padding = h_value; for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; arrange_workspace(output_get_active_workspace(output)); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/titlebar_separator.c b/sway/commands/titlebar_separator.c index a5ec97f6..05b15b92 100644 --- a/sway/commands/titlebar_separator.c +++ b/sway/commands/titlebar_separator.c @@ -1,7 +1,6 @@ #include #include #include "sway/commands.h" -#include "util.h" struct cmd_results *cmd_titlebar_separator(int argc, char **argv) { struct cmd_results *error = NULL; diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 19274dfb..4aba5bae 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -8,9 +7,13 @@ #include "log.h" #include "stringop.h" -static void remove_all_marks_iterator(struct sway_container *con, void *data) { +static void remove_mark(struct sway_container *con) { container_clear_marks(con); - container_update_marks_textures(con); + container_update_marks(con); +} + +static void remove_all_marks_iterator(struct sway_container *con, void *data) { + remove_mark(con); } // unmark Remove all marks from all views @@ -38,8 +41,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { } } else if (con && !mark) { // Clear all marks from the given container - container_clear_marks(con); - container_update_marks_textures(con); + remove_mark(con); } else if (!con && mark) { // Remove mark from whichever container has it container_find_and_unmark(mark); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 03e488ba..a14ebb20 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index 6ca26923..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; @@ -20,6 +20,8 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { xwayland = XWAYLAND_MODE_DISABLED; } + // config->xwayland is reset to the previous value on reload in + // load_main_config() if (config->reloading && config->xwayland != xwayland) { return cmd_results_new(CMD_FAILURE, "xwayland can only be enabled/disabled at launch"); diff --git a/sway/config.c b/sway/config.c index 616965c1..93259ade 100644 --- a/sway/config.c +++ b/sway/config.c @@ -1,3 +1,4 @@ +#undef _POSIX_C_SOURCE #define _XOPEN_SOURCE 700 // for realpath #include #include @@ -23,6 +24,7 @@ #include "sway/criteria.h" #include "sway/layer_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" @@ -37,19 +39,26 @@ struct sway_config *config = NULL; static struct xkb_state *keysym_translation_state_create( - struct xkb_rule_names rules) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); + struct xkb_rule_names rules, uint32_t context_flags) { + struct xkb_context *context = xkb_context_new(context_flags | XKB_CONTEXT_NO_SECURE_GETENV); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - xkb_context_unref(context); + if (xkb_keymap == NULL) { + sway_log(SWAY_ERROR, "Failed to compile keysym translation XKB keymap"); + return NULL; + } + return xkb_state_new(xkb_keymap); } static void keysym_translation_state_destroy( struct xkb_state *state) { + if (state == NULL) { + return; + } xkb_keymap_unref(xkb_state_get_keymap(state)); xkb_state_unref(state); } @@ -349,6 +358,10 @@ static void config_defaults(struct sway_config *config) { color_to_rgba(config->dim_inactive_colors.unfocused, 0x000000FF); color_to_rgba(config->dim_inactive_colors.urgent, 0x900000FF); + config->blur_enabled = false; + config->blur_xray = false; + config->blur_data = blur_data_get_default(); + config->shadow_enabled = false; config->shadows_on_csd_enabled = false; config->shadow_blur_sigma = 20.0f; @@ -357,24 +370,23 @@ static void config_defaults(struct sway_config *config) { color_to_rgba(config->shadow_color, 0x0000007F); color_to_rgba(config->shadow_inactive_color, 0x0000007F); - config->blur_enabled = false; - config->blur_xray = false; - config->blur_params.num_passes = 2; - config->blur_params.radius = 5; - config->blur_params.noise = 0.02; - config->blur_params.brightness = 0.9; - config->blur_params.contrast = 0.9; - config->blur_params.saturation = 1.1; - config->titlebar_separator = true; config->scratchpad_minimize = false; - if (!(config->layer_criteria = create_list())) goto cleanup; + if (!(config->layer_criteria = create_list())) { + goto cleanup; + } // The keysym to keycode translation struct xkb_rule_names rules = {0}; - config->keysym_translation_state = - keysym_translation_state_create(rules); + config->keysym_translation_state = keysym_translation_state_create(rules, 0); + if (config->keysym_translation_state == NULL) { + config->keysym_translation_state = keysym_translation_state_create(rules, + XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + } + if (config->keysym_translation_state == NULL) { + goto cleanup; + } return; cleanup: @@ -389,13 +401,7 @@ static char *config_path(const char *prefix, const char *config_folder) { if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { return NULL; } - - const char *filename = "config"; - - size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); - char *path = calloc(size, sizeof(char)); - snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); - return path; + return format_str("%s/%s/config", prefix, config_folder); } static char *get_config_path(void) { @@ -405,10 +411,7 @@ static char *get_config_path(void) { const char *config_home = getenv("XDG_CONFIG_HOME"); if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { - size_t size_fallback = 1 + strlen(home) + strlen("/.config"); - config_home_fallback = calloc(size_fallback, sizeof(char)); - if (config_home_fallback != NULL) - snprintf(config_home_fallback, size_fallback, "%s/.config", home); + config_home_fallback = format_str("%s/.config", home); config_home = config_home_fallback; } @@ -536,56 +539,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reading = true; - // Read security configs - // TODO: Security - bool success = true; - /* - DIR *dir = opendir(SYSCONFDIR "/sway/security.d"); - if (!dir) { - sway_log(SWAY_ERROR, - "%s does not exist, sway will have no security configuration" - " and will probably be broken", SYSCONFDIR "/sway/security.d"); - } else { - list_t *secconfigs = create_list(); - char *base = SYSCONFDIR "/sway/security.d/"; - struct dirent *ent = readdir(dir); - struct stat s; - while (ent != NULL) { - char *_path = malloc(strlen(ent->d_name) + strlen(base) + 1); - strcpy(_path, base); - strcat(_path, ent->d_name); - lstat(_path, &s); - if (S_ISREG(s.st_mode) && ent->d_name[0] != '.') { - list_add(secconfigs, _path); - } - else { - free(_path); - } - ent = readdir(dir); - } - closedir(dir); - - list_qsort(secconfigs, qstrcmp); - for (int i = 0; i < secconfigs->length; ++i) { - char *_path = secconfigs->items[i]; - if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || - (((s.st_mode & 0777) != 0644) && - (s.st_mode & 0777) != 0444)) { - sway_log(SWAY_ERROR, - "Refusing to load %s - it must be owned by root " - "and mode 644 or 444", _path); - success = false; - } else { - success = success && load_config(_path, config); - } - } - - list_free_items_and_destroy(secconfigs); - } - */ - - success = success && load_config(path, config, - &config->swaynag_config_errors); + bool success = load_config(path, config, &config->swaynag_config_errors); if (validating) { free_config(config); @@ -596,7 +550,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { // Only really necessary if not explicitly `font` is set in the config. config_update_font_height(); - if (is_active && !validating) { + if (!validating) { input_manager_verify_fallback_seat(); for (int i = 0; i < config->input_configs->length; i++) { @@ -613,12 +567,14 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - reset_outputs(); spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.client != NULL) { - swaynag_show(&config->swaynag_config_errors); + if (is_active) { + request_modeset(); + if (config->swaynag_config_errors.client != NULL) { + swaynag_show(&config->swaynag_config_errors); + } } } @@ -1079,8 +1035,12 @@ void translate_keysyms(struct input_config *input_config) { struct xkb_rule_names rules = {0}; input_config_fill_rule_names(input_config, &rules); - config->keysym_translation_state = - keysym_translation_state_create(rules); + config->keysym_translation_state = keysym_translation_state_create(rules, 0); + if (config->keysym_translation_state == NULL) { + sway_log(SWAY_ERROR, "Failed to create keysym translation XKB state " + "for device '%s'", input_config->identifier); + return; + } for (int i = 0; i < config->modes->length; ++i) { struct sway_mode *mode = config->modes->items[i]; @@ -1101,22 +1061,3 @@ void translate_keysyms(struct input_config *input_config) { sway_log(SWAY_DEBUG, "Translated keysyms using config for device '%s'", input_config->identifier); } - -int config_get_blur_size() { - return pow(2, config->blur_params.num_passes + 1) * config->blur_params.radius; -} - -bool config_should_parameters_blur() { - return config->blur_params.radius > 0 && config->blur_params.num_passes > 0; -} - -bool config_should_parameters_blur_effects() { - return config->blur_params.brightness != 1.0f - || config->blur_params.saturation != 1.0f - || config->blur_params.contrast != 1.0f - || config->blur_params.noise > 0.0f; -} - -bool config_should_parameters_shadow() { - return config->shadow_blur_sigma > 0 && config->shadow_color[3] > 0.0; -} diff --git a/sway/config/bar.c b/sway/config/bar.c index a8389244..ecefb61a 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -13,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 44c2be28..e5694eff 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -1,9 +1,9 @@ -#define _POSIX_C_SOURCE 200809L #include #include #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) { @@ -28,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; @@ -55,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 3c1822d8..f8922ea5 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -10,9 +9,16 @@ #include #include #include +#include +#include #include "sway/config.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/layers.h" +#include "sway/lock.h" #include "sway/output.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "log.h" #include "util.h" @@ -21,13 +27,6 @@ #include #endif -int output_name_cmp(const void *item, const void *data) { - const struct output_config *output = item; - const char *name = data; - - return strcmp(output->name, name); -} - void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; @@ -75,11 +74,79 @@ 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; + oc->allow_tearing = -1; return oc; } -void merge_output_config(struct output_config *dst, struct output_config *src) { +// supersede_output_config clears all fields in dst that were set in src +static void supersede_output_config(struct output_config *dst, struct output_config *src) { + if (src->enabled != -1) { + dst->enabled = -1; + } + if (src->width != -1) { + dst->width = -1; + } + if (src->height != -1) { + dst->height = -1; + } + if (src->x != -1) { + dst->x = -1; + } + if (src->y != -1) { + dst->y = -1; + } + if (src->scale != -1) { + dst->scale = -1; + } + if (src->scale_filter != SCALE_FILTER_DEFAULT) { + dst->scale_filter = SCALE_FILTER_DEFAULT; + } + if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { + dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + } + if (src->refresh_rate != -1) { + dst->refresh_rate = -1; + } + if (src->custom_mode != -1) { + dst->custom_mode = -1; + } + if (src->drm_mode.type != (uint32_t) -1) { + dst->drm_mode.type = -1; + } + if (src->transform != -1) { + dst->transform = -1; + } + if (src->max_render_time != -1) { + dst->max_render_time = -1; + } + if (src->adaptive_sync != -1) { + dst->adaptive_sync = -1; + } + if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + } + if (src->background) { + free(dst->background); + dst->background = NULL; + } + if (src->background_option) { + free(dst->background_option); + dst->background_option = NULL; + } + if (src->background_fallback) { + free(dst->background_fallback); + dst->background_fallback = NULL; + } + if (src->power != -1) { + dst->power = -1; + } +} + +// merge_output_config sets all fields in dst that were set in src +static void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->enabled != -1) { dst->enabled = src->enabled; } @@ -125,6 +192,14 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { 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); @@ -140,107 +215,64 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->power != -1) { dst->power = src->power; } -} - -static void merge_wildcard_on_all(struct output_config *wildcard) { - for (int i = 0; i < config->output_configs->length; i++) { - struct output_config *oc = config->output_configs->items[i]; - if (strcmp(wildcard->name, oc->name) != 0) { - sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); - merge_output_config(oc, wildcard); - } + if (src->allow_tearing != -1) { + dst->allow_tearing = src->allow_tearing; } } -static void merge_id_on_name(struct output_config *oc) { - struct sway_output *output = all_output_by_name_or_id(oc->name); - if (output == NULL) { - return; - } - - const char *name = output->wlr_output->name; - char id[128]; - output_get_identifier(id, sizeof(id), output); - - char *id_on_name = format_str("%s on %s", id, name); - if (!id_on_name) { - return; - } - - int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); - merge_output_config(config->output_configs->items[i], oc); - } else { - // If both a name and identifier config, exist generate an id on name - int ni = list_seq_find(config->output_configs, output_name_cmp, name); - int ii = list_seq_find(config->output_configs, output_name_cmp, id); - if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) - || (ii >= 0 && strcmp(oc->name, name) == 0)) { - struct output_config *ion_oc = new_output_config(id_on_name); - if (ni >= 0) { - merge_output_config(ion_oc, config->output_configs->items[ni]); - } - if (ii >= 0) { - merge_output_config(ion_oc, config->output_configs->items[ii]); - } - merge_output_config(ion_oc, oc); - list_add(config->output_configs, ion_oc); - sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" - " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " - "transform %d) (bg %s %s) (power %d) (max render time: %d)", - ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, - ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, - ion_oc->transform, ion_oc->background, - ion_oc->background_option, ion_oc->power, - ion_oc->max_render_time); - } - } - free(id_on_name); -} - -struct output_config *store_output_config(struct output_config *oc) { +void store_output_config(struct output_config *oc) { + bool merged = false; bool wildcard = strcmp(oc->name, "*") == 0; - if (wildcard) { - merge_wildcard_on_all(oc); - } else { - merge_id_on_name(oc); + struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name); + + char id[128]; + if (output) { + output_get_identifier(id, sizeof(id), output); } - int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of existing output config"); - struct output_config *current = config->output_configs->items[i]; - merge_output_config(current, oc); - free_output_config(oc); - oc = current; - } else if (!wildcard) { - sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); - i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of output * config"); - struct output_config *current = new_output_config(oc->name); - merge_output_config(current, config->output_configs->items[i]); - merge_output_config(current, oc); - free_output_config(oc); - oc = current; + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *old = config->output_configs->items[i]; + + // If the old config matches the new config's name, regardless of + // whether it was name or identifier, merge on top of the existing + // config. If the new config is a wildcard, this also merges on top of + // old wildcard configs. + if (strcmp(old->name, oc->name) == 0) { + merge_output_config(old, oc); + merged = true; + continue; + } + + // If the new config is a wildcard config we supersede all non-wildcard + // configs. Old wildcard configs have already been handled above. + if (wildcard) { + supersede_output_config(old, oc); + continue; + } + + // If the new config matches an output's name, and the old config + // matches on that output's identifier, supersede it. + if (output && strcmp(old->name, id) == 0 && + strcmp(oc->name, output->wlr_output->name) == 0) { + supersede_output_config(old, oc); } - list_add(config->output_configs, oc); - } else { - // New wildcard config. Just add it - sway_log(SWAY_DEBUG, "Adding output * config"); - list_add(config->output_configs, oc); } sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " - "(max render time: %d)", + "(max render time: %d) (allow tearing: %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->transform, oc->background, oc->background_option, oc->power, - oc->max_render_time); + oc->max_render_time, oc->allow_tearing); - return oc; + // If the configuration was not merged into an existing configuration, add + // it to the list. Otherwise we're done with it and can free it. + if (!merged) { + list_add(config->output_configs, oc); + } else { + free_output_config(oc); + } } static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, @@ -253,7 +285,6 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { - sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); wlr_output_state_set_custom_mode(pending, width, height, refresh_rate > 0 ? mhz : 0); return; @@ -273,10 +304,7 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } } } - if (best) { - sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", - best->width, best->height, best->refresh / 1000.f, output->name); - } else { + if (!best) { best = wlr_output_preferred_mode(output); sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " "applying preferred mode (%dx%d@%.3fHz)", @@ -293,7 +321,6 @@ static void set_modeline(struct wlr_output *output, sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; } - sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); if (mode) { wlr_output_state_set_mode(pending, mode); @@ -359,7 +386,6 @@ static int compute_default_scale(struct wlr_output *output, double dpi_x = (double) width / (output->phys_width / MM_PER_INCH); double dpi_y = (double) height / (output->phys_height / MM_PER_INCH); - sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y); if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) { return 1; } @@ -367,22 +393,24 @@ static int compute_default_scale(struct wlr_output *output, return 2; } -/* Lists of formats to try, in order, when a specific render bit depth has - * been asked for. The second to last format in each list should always - * be XRGB8888, as a reliable backup in case the others are not available; - * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ -static const uint32_t *bit_depth_preferences[] = { - [RENDER_BIT_DEPTH_8] = (const uint32_t []){ - DRM_FORMAT_XRGB8888, - DRM_FORMAT_INVALID, - }, - [RENDER_BIT_DEPTH_10] = (const uint32_t []){ - DRM_FORMAT_XRGB2101010, - DRM_FORMAT_XBGR2101010, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_INVALID, - }, -}; +static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { + if (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) { + return RENDER_BIT_DEPTH_10; + } else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) { + return RENDER_BIT_DEPTH_8; + } else if (render_format == DRM_FORMAT_RGB565) { + return RENDER_BIT_DEPTH_6; + } + return RENDER_BIT_DEPTH_DEFAULT; +} + +static bool render_format_is_bgr(uint32_t fmt) { + return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888; +} + +static bool output_config_is_disabling(struct output_config *oc) { + return oc && (!oc->enabled || oc->power == 0); +} static void queue_output_config(struct output_config *oc, struct sway_output *output, struct wlr_output_state *pending) { @@ -392,143 +420,83 @@ static void queue_output_config(struct output_config *oc, struct wlr_output *wlr_output = output->wlr_output; - if (oc && (!oc->enabled || oc->power == 0)) { - sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); + if (output_config_is_disabling(oc)) { wlr_output_state_set_enabled(pending, false); return; } - - sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); wlr_output_state_set_enabled(pending, true); if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { - sway_log(SWAY_DEBUG, "Set %s modeline", - wlr_output->name); set_modeline(wlr_output, pending, &oc->drm_mode); } else if (oc && oc->width > 0 && oc->height > 0) { - sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", - wlr_output->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, pending, oc->width, oc->height, oc->refresh_rate, oc->custom_mode == 1); } else if (!wl_list_empty(&wlr_output->modes)) { - sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); wlr_output_state_set_mode(pending, preferred_mode); - - if (!wlr_output_test_state(wlr_output, pending)) { - sway_log(SWAY_DEBUG, "Preferred mode rejected, " - "falling back to another mode"); - struct wlr_output_mode *mode; - wl_list_for_each(mode, &wlr_output->modes, link) { - if (mode == preferred_mode) { - continue; - } - - wlr_output_state_set_mode(pending, mode); - if (wlr_output_test_state(wlr_output, pending)) { - break; - } - } - } } - if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { - sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, - sway_wl_output_subpixel_to_string(oc->subpixel)); + if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { wlr_output_state_set_subpixel(pending, oc->subpixel); + } else { + wlr_output_state_set_subpixel(pending, output->detected_subpixel); } - enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { - tr = oc->transform; + wlr_output_state_set_transform(pending, oc->transform); #if WLR_HAS_DRM_BACKEND } else if (wlr_output_is_drm(wlr_output)) { - tr = wlr_drm_connector_get_panel_orientation(wlr_output); - sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); + wlr_output_state_set_transform(pending, + wlr_drm_connector_get_panel_orientation(wlr_output)); #endif - } - if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); - wlr_output_state_set_transform(pending, tr); + } else { + wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL); } - // Apply the scale last before the commit, because the scale auto-detection - // reads the pending output size - float scale; + // Apply the scale after sorting out the mode, because the scale + // auto-detection reads the pending output size if (oc && oc->scale > 0) { - scale = oc->scale; - // The factional-scale-v1 protocol uses increments of 120ths to send // the scale factor to the client. Adjust the scale so that we use the // same value as the clients'. - float adjusted_scale = round(scale * 120) / 120; - if (scale != adjusted_scale) { - sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", - scale, adjusted_scale); - scale = adjusted_scale; - } + wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120); } else { - scale = compute_default_scale(wlr_output, pending); - sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); - } - if (scale != wlr_output->scale) { - sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); - wlr_output_state_set_scale(pending, scale); + wlr_output_state_set_scale(pending, + compute_default_scale(wlr_output, pending)); } - if (oc && oc->adaptive_sync != -1) { - sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, - oc->adaptive_sync); - wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); - if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { - sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); + if (wlr_output->adaptive_sync_supported) { + if (oc && oc->adaptive_sync != -1) { + wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + } else { wlr_output_state_set_adaptive_sync_enabled(pending, false); } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; - assert(fmts); - - for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { - wlr_output_state_set_render_format(pending, fmts[i]); - if (wlr_output_test_state(wlr_output, pending)) { - break; - } - - sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " - "failed to work, falling back to next in " - "list, 0x%08x", fmts[i], fmts[i + 1]); + if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && + bit_depth_from_format(output->wlr_output->render_format) == oc->render_bit_depth) { + // 10-bit was set successfully before, try to save some tests by reusing the format + wlr_output_state_set_render_format(pending, output->wlr_output->render_format); + } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); + } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_6){ + wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } } -bool apply_output_config(struct output_config *oc, struct sway_output *output) { +static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->fallback_output) { return false; } struct wlr_output *wlr_output = output->wlr_output; - - // Flag to prevent the output mode event handler from calling us - output->enabling = (!oc || oc->enabled); - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - - sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); - if (!wlr_output_commit_state(wlr_output, &pending)) { - // Failed to commit output changes, maybe the output is missing a CRTC. - // Leave the output disabled for now and try again when the output gets - // the mode we asked for. - sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); - output->enabling = false; - return false; - } - - output->enabling = false; - if (oc && !oc->enabled) { sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); if (output->enabled) { @@ -538,27 +506,23 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } - if (config->reloading) { - output_damage_whole(output); + enum scale_filter_mode scale_filter_old = output->scale_filter; + enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT; + switch (scale_filter_new) { + case SCALE_FILTER_DEFAULT: + case SCALE_FILTER_SMART: + output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? + SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; + break; + case SCALE_FILTER_LINEAR: + case SCALE_FILTER_NEAREST: + output->scale_filter = scale_filter_new; + break; } - - if (oc) { - enum scale_filter_mode scale_filter_old = output->scale_filter; - switch (oc->scale_filter) { - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? - SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; - break; - case SCALE_FILTER_LINEAR: - case SCALE_FILTER_NEAREST: - output->scale_filter = oc->scale_filter; - break; - } - if (scale_filter_old != output->scale_filter) { - sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, - sway_output_scale_filter_to_string(output->scale_filter)); - } + if (scale_filter_old != output->scale_filter) { + sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, + sway_output_scale_filter_to_string(output->scale_filter)); + wlr_damage_ring_add_whole(&output->scene_output->damage_ring); } // Find position for it @@ -569,196 +533,480 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - // Update output->{lx, ly, width, height} - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; - if (!output->enabled) { output_enable(output); } - if (oc && oc->max_render_time >= 0) { - sway_log(SWAY_DEBUG, "Set %s max render time to %d", - oc->name, oc->max_render_time); - 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; + } else { + wlr_color_transform_unref(output->color_transform); + output->color_transform = NULL; } + output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; + output->allow_tearing = oc && oc->allow_tearing > 0; + + return true; +} + +static void output_update_position(struct sway_output *output) { + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; +} + +// find_output_config_from_list returns a merged output_config containing all +// stored configuration that applies to the specified output. +static struct output_config *find_output_config_from_list( + struct output_config **configs, size_t configs_len, + struct sway_output *sway_output) { + const char *name = sway_output->wlr_output->name; + struct output_config *result = new_output_config(name); + if (result == NULL) { + return NULL; + } + + char id[128]; + output_get_identifier(id, sizeof(id), sway_output); + + // We take a new config and merge on top, in order, the wildcard config, + // output config by name, and output config by identifier to form the final + // config. If there are multiple matches, they are merged in order. + struct output_config *oc = NULL; + const char *names[] = {"*", name, id, NULL}; + for (const char **name = &names[0]; *name; name++) { + for (size_t idx = 0; idx < configs_len; idx++) { + oc = configs[idx]; + if (strcmp(oc->name, *name) == 0) { + merge_output_config(result, oc); + } + } + } + + return result; +} + +struct output_config *find_output_config(struct sway_output *sway_output) { + return find_output_config_from_list( + (struct output_config **)config->output_configs->items, + config->output_configs->length, sway_output); +} + +static bool config_has_manual_mode(struct output_config *oc) { + if (!oc) { + return false; + } + if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { + return true; + } else if (oc->width > 0 && oc->height > 0) { + return true; + } + return false; +} + +/** + * An output config pre-matched to an output + */ +struct matched_output_config { + struct sway_output *output; + struct output_config *config; +}; + +struct search_context { + struct wlr_output_swapchain_manager *swapchain_mgr; + struct wlr_backend_output_state *states; + struct matched_output_config *configs; + size_t configs_len; + bool degrade_to_off; +}; + +static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) { + sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name); + if (state->committed & WLR_OUTPUT_STATE_ENABLED) { + sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); + } + if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + char *format_name = drmGetFormatName(state->render_format); + sway_log(SWAY_DEBUG, " render_format: %s", format_name); + free(format_name); + } + if (state->committed & WLR_OUTPUT_STATE_MODE) { + if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { + sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz", + state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh); + } else { + sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s", + state->mode->width, state->mode->height, state->mode->refresh, + state->mode->preferred ? " (preferred)" : ""); + } + } + if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { + sway_log(SWAY_DEBUG, " adaptive_sync: %s", + state->adaptive_sync_enabled ? "enabled": "disabled"); + } + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + sway_log(SWAY_DEBUG, " scale: %f", state->scale); + } + if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { + sway_log(SWAY_DEBUG, " subpixel: %s", + sway_wl_output_subpixel_to_string(state->subpixel)); + } +} + +static bool search_valid_config(struct search_context *ctx, size_t output_idx); + +static void reset_output_state(struct wlr_output_state *state) { + wlr_output_state_finish(state); + wlr_output_state_init(state); + state->committed = 0; +} + +static void clear_later_output_states(struct wlr_backend_output_state *states, + size_t configs_len, size_t output_idx) { + + // Clear and disable all output states after this one to avoid conflict + // with previous tests. + for (size_t idx = output_idx+1; idx < configs_len; idx++) { + struct wlr_backend_output_state *backend_state = &states[idx]; + struct wlr_output_state *state = &backend_state->base; + + reset_output_state(state); + wlr_output_state_set_enabled(state, false); + } +} + +static bool search_finish(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; + struct wlr_output *wlr_output = backend_state->output; + + clear_later_output_states(ctx->states, ctx->configs_len, output_idx); + dump_output_state(wlr_output, state); + return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) && + search_valid_config(ctx, output_idx+1); +} + +static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[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; + } + } + + wlr_output_state_set_adaptive_sync_enabled(state, false); + return search_finish(ctx, output_idx); +} + +static bool search_mode(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + + // We only search for mode if one is not explicitly specified in the config + if (config_has_manual_mode(cfg->config)) { + return search_adaptive_sync(ctx, output_idx); + } + + struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); + if (preferred_mode) { + wlr_output_state_set_mode(state, preferred_mode); + if (search_adaptive_sync(ctx, output_idx)) { + return true; + } + } + + if (wl_list_empty(&wlr_output->modes)) { + state->committed &= ~WLR_OUTPUT_STATE_MODE; + return search_adaptive_sync(ctx, output_idx); + } + + struct wlr_output_mode *mode; + wl_list_for_each(mode, &backend_state->output->modes, link) { + if (mode == preferred_mode) { + continue; + } + wlr_output_state_set_mode(state, mode); + if (search_adaptive_sync(ctx, output_idx)) { + return true; + } + } + + return false; +} + +static bool search_render_format(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + + uint32_t fmts[] = { + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_INVALID, + }; + if (render_format_is_bgr(wlr_output->render_format)) { + // Start with BGR in the unlikely event that we previously required it. + fmts[0] = DRM_FORMAT_XBGR2101010; + fmts[1] = DRM_FORMAT_XRGB2101010; + } + + const struct wlr_drm_format_set *primary_formats = + wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); + enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; + if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + needed_bits = cfg->config->render_bit_depth; + } + for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { + enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]); + if (needed_bits < format_bits) { + continue; + } + if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { + // This is not a supported format for this output + continue; + } + wlr_output_state_set_render_format(state, fmts[idx]); + if (search_mode(ctx, output_idx)) { + return true; + } + } + return false; +} + +static bool search_valid_config(struct search_context *ctx, size_t output_idx) { + if (output_idx >= ctx->configs_len) { + // We reached the end of the search, all good! + return true; + } + + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + + if (!output_config_is_disabling(cfg->config)) { + // Search through our possible configurations, doing a depth-first + // through render_format, modes, adaptive_sync and the next output's + // config. + queue_output_config(cfg->config, cfg->output, &backend_state->base); + if (search_render_format(ctx, output_idx)) { + return true; + } else if (!ctx->degrade_to_off) { + return false; + } + // We could not get anything to work, try to disable this output to see + // if we can at least make the outputs before us work. + sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling", + wlr_output->name); + reset_output_state(state); + } + + wlr_output_state_set_enabled(state, false); + return search_finish(ctx, output_idx); +} + +static int compare_matched_output_config_priority(const void *a, const void *b) { + + const struct matched_output_config *amc = a; + const struct matched_output_config *bmc = b; + bool a_disabling = output_config_is_disabling(amc->config); + bool b_disabling = output_config_is_disabling(bmc->config); + bool a_enabled = amc->output->enabled; + bool b_enabled = bmc->output->enabled; + + // We want to give priority to existing enabled outputs. To do so, we want + // the configuration order to be: + // 1. Existing, enabled outputs + // 2. Outputs that need to be enabled + // 3. Disabled or disabling outputs + if (a_enabled && !a_disabling) { + return -1; + } else if (b_enabled && !b_disabling) { + return 1; + } else if (b_disabling && !a_disabling) { + return -1; + } else if (a_disabling && !b_disabling) { + return 1; + } + return 0; +} + +static void sort_output_configs_by_priority( + struct matched_output_config *configs, size_t configs_len) { + qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); +} + +static bool apply_resolved_output_configs(struct matched_output_config *configs, + size_t configs_len, bool test_only, bool degrade_to_off) { + struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); + if (!states) { + return false; + } + + sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + backend_state->output = cfg->output->wlr_output; + wlr_output_state_init(&backend_state->base); + + queue_output_config(cfg->config, cfg->output, &backend_state->base); + dump_output_state(cfg->output->wlr_output, &backend_state->base); + } + + struct wlr_output_swapchain_manager swapchain_mgr; + wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); + + bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks"); + struct search_context ctx = { + .swapchain_mgr = &swapchain_mgr, + .states = states, + .configs = configs, + .configs_len = configs_len, + .degrade_to_off = degrade_to_off, + }; + if (!search_valid_config(&ctx, 0)) { + sway_log(SWAY_ERROR, "Search for valid config failed"); + goto out; + } + } + + if (test_only) { + // The swapchain manager already did a test for us + goto out; + } + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + 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; + if (!wlr_scene_output_build_state(scene_output, state, &opts)) { + sway_log(SWAY_ERROR, "Building output state for '%s' failed", + backend_state->output->name); + goto out; + } + } + + ok = wlr_backend_commit(server.backend, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Backend commit failed"); + goto out; + } + + sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); + + wlr_output_swapchain_manager_apply(&swapchain_mgr); + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + sway_log(SWAY_DEBUG, "Finalizing config for %s", + cfg->output->wlr_output->name); + finalize_output_config(cfg->config, cfg->output); + } + + // Output layout being applied in finalize_output_config can shift outputs + // around, so we do a second pass to update positions and arrange. + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + output_update_position(cfg->output); + arrange_layers(cfg->output); + } + + arrange_root(); + arrange_locks(); + update_output_manager_config(&server); + transaction_commit_dirty(); + +out: + wlr_output_swapchain_manager_finish(&swapchain_mgr); + for (size_t idx = 0; idx < configs_len; idx++) { + struct wlr_backend_output_state *backend_state = &states[idx]; + wlr_output_state_finish(&backend_state->base); + } + free(states); + // Reconfigure all devices, since input config may have been applied before // this output came online, and some config items (like map_to_output) are // dependent on an output being present. input_manager_configure_all_input_mappings(); // Reconfigure the cursor images, since the scale may have changed. input_manager_configure_xcursor(); - return true; -} - -bool test_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->fallback_output) { - return false; - } - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - return wlr_output_test_state(output->wlr_output, &pending); -} - -static void default_output_config(struct output_config *oc, - struct wlr_output *wlr_output) { - oc->enabled = 1; - oc->power = 1; - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - if (mode != NULL) { - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } - oc->x = oc->y = -1; - oc->scale = 0; // auto - oc->scale_filter = SCALE_FILTER_DEFAULT; - struct sway_output *output = wlr_output->data; - oc->subpixel = output->detected_subpixel; - oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; - oc->max_render_time = 0; -} - -static struct output_config *get_output_config(char *identifier, - struct sway_output *sway_output) { - const char *name = sway_output->wlr_output->name; - - struct output_config *oc_id_on_name = NULL; - struct output_config *oc_name = NULL; - struct output_config *oc_id = NULL; - - char *id_on_name = format_str("%s on %s", identifier, name); - int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); - if (i >= 0) { - oc_id_on_name = config->output_configs->items[i]; - } else { - i = list_seq_find(config->output_configs, output_name_cmp, name); - if (i >= 0) { - oc_name = config->output_configs->items[i]; - } - - i = list_seq_find(config->output_configs, output_name_cmp, identifier); - if (i >= 0) { - oc_id = config->output_configs->items[i]; - } - } - - struct output_config *result = new_output_config("temp"); - if (config->reloading) { - default_output_config(result, sway_output->wlr_output); - } - if (oc_id_on_name) { - // Already have an identifier on name config, use that - free(result->name); - result->name = strdup(id_on_name); - merge_output_config(result, oc_id_on_name); - } else if (oc_name && oc_id) { - // Generate a config named ` on ` which contains a - // merged copy of the identifier on name. This will make sure that both - // identifier and name configs are respected, with identifier getting - // priority - struct output_config *temp = new_output_config(id_on_name); - merge_output_config(temp, oc_name); - merge_output_config(temp, oc_id); - list_add(config->output_configs, temp); - - free(result->name); - result->name = strdup(id_on_name); - merge_output_config(result, temp); - - sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" - " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" - " (power %d) (max render time: %d)", result->name, result->enabled, - result->width, result->height, result->refresh_rate, - result->x, result->y, result->scale, result->transform, - result->background, result->background_option, result->power, - result->max_render_time); - } else if (oc_name) { - // No identifier config, just return a copy of the name config - free(result->name); - result->name = strdup(name); - merge_output_config(result, oc_name); - } else if (oc_id) { - // No name config, just return a copy of the identifier config - free(result->name); - result->name = strdup(identifier); - merge_output_config(result, oc_id); - } else { - i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - // No name or identifier config, but there is a wildcard config - free(result->name); - result->name = strdup("*"); - merge_output_config(result, config->output_configs->items[i]); - } else if (!config->reloading) { - // No name, identifier, or wildcard config. Since we are not - // reloading with defaults, the output config will be empty, so - // just return NULL - free_output_config(result); - result = NULL; - } - } - - free(id_on_name); - return result; -} - -struct output_config *find_output_config(struct sway_output *output) { - char id[128]; - output_get_identifier(id, sizeof(id), output); - return get_output_config(id, output); -} - -void apply_output_config_to_outputs(struct output_config *oc) { - // Try to find the output container and apply configuration now. If - // this is during startup then there will be no container and config - // will be applied during normal "new output" event from wlroots. - bool wildcard = strcmp(oc->name, "*") == 0; - struct sway_output *sway_output, *tmp; - wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { - if (output_match_name_or_id(sway_output, oc->name)) { - char id[128]; - output_get_identifier(id, sizeof(id), sway_output); - struct output_config *current = get_output_config(id, sway_output); - if (!current) { - // No stored output config matched, apply oc directly - sway_log(SWAY_DEBUG, "Applying oc directly"); - current = new_output_config(oc->name); - merge_output_config(current, oc); - } - apply_output_config(current, sway_output); - free_output_config(current); - - if (!wildcard) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } - } - } struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); cursor_rebase(seat->cursor); } + + return ok; } -void reset_outputs(void) { - struct output_config *oc = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - oc = config->output_configs->items[i]; - } else { - oc = store_output_config(new_output_config("*")); +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off) { + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return false; } - apply_output_config_to_outputs(oc); + + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; + continue; + } + + struct matched_output_config *config = &configs[config_idx++]; + config->output = sway_output; + config->config = find_output_config_from_list(ocs, ocs_len, sway_output); + } + + sort_output_configs_by_priority(configs, configs_len); + bool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + free_output_config(cfg->config); + } + free(configs); + return ok; +} + +void apply_stored_output_configs(void) { + apply_output_configs((struct output_config **)config->output_configs->items, + config->output_configs->length, false, true); } void free_output_config(struct output_config *oc) { @@ -768,6 +1016,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/config/seat.c b/sway/config/seat.c index 6d5d91ae..f2326189 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/criteria.c b/sway/criteria.c index 78ea8b8a..2b7290c0 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -8,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" @@ -23,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 @@ -91,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); @@ -111,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; @@ -252,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) { @@ -429,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; @@ -462,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, @@ -488,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) { @@ -567,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; @@ -675,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/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index c8d4502c..00000000 --- a/sway/desktop/desktop.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "sway/tree/container.h" -#include "sway/desktop.h" -#include "sway/output.h" - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, - output->wlr_output, &output_box); - output_damage_surface(output, lx - output_box.x, - ly - output_box.y, surface, whole); - } -} - -void desktop_damage_whole_container(struct sway_container *con) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, con); - } -} - -void desktop_damage_box(struct wlr_box *box) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_box(output, box); - } -} - -void desktop_damage_view(struct sway_view *view) { - desktop_damage_whole_container(view->container); - struct wlr_box box = { - .x = view->container->current.content_x - view->geometry.x, - .y = view->container->current.content_y - view->geometry.y, - .width = view->surface->current.width, - .height = view->surface->current.height, - }; - desktop_damage_box(&box); -} diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 0c02b997..2362e1ba 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -1,10 +1,10 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include #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/layer_shell.c b/sway/desktop/layer_shell.c index 81429053..c4ea1bef 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -1,13 +1,15 @@ -#include +#include #include #include #include #include +#include #include #include #include +#include #include "log.h" -#include "sway/layer_criteria.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -15,10 +17,8 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -#include "wlr-layer-shell-unstable-v1-protocol.h" struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( struct wlr_surface *surface) { @@ -53,176 +53,74 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } while (true); } -static void layer_parse_criteria(struct sway_layer_surface *sway_layer) { - enum zwlr_layer_shell_v1_layer layer = sway_layer->layer; - if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { - return; - } - - list_t *criterias = layer_criterias_for_sway_layer_surface(sway_layer); - for (int i = 0; i < criterias->length; i++) { - struct layer_criteria *criteria = criterias->items[i]; - layer_criteria_parse(sway_layer, criteria); - } - list_free(criterias); -} - -static void apply_exclusive(struct wlr_box *usable_area, - uint32_t anchor, int32_t exclusive, - int32_t margin_top, int32_t margin_right, - int32_t margin_bottom, int32_t margin_left) { - if (exclusive <= 0) { - return; - } - struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; - } edges[] = { - // Top - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .positive_axis = &usable_area->y, - .negative_axis = &usable_area->height, - .margin = margin_top, - }, - // Bottom - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->height, - .margin = margin_bottom, - }, - // Left - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = &usable_area->x, - .negative_axis = &usable_area->width, - .margin = margin_left, - }, - // Right - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->width, - .margin = margin_right, - }, - }; - for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { - if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) - && exclusive + edges[i].margin > 0) { - if (edges[i].positive_axis) { - *edges[i].positive_axis += exclusive + edges[i].margin; - } - if (edges[i].negative_axis) { - *edges[i].negative_axis -= exclusive + edges[i].margin; - } - break; - } +void layer_apply_criteria(struct sway_layer_surface *surface, struct layer_criteria *criteria) { + if (criteria) { + surface->corner_radius = criteria->corner_radius; + surface->blur_enabled = criteria->blur_enabled; + surface->blur_xray = criteria->blur_xray; + surface->blur_ignore_transparent = criteria->blur_ignore_transparent; + surface->shadow_enabled = criteria->shadow_enabled; + } else { + // Reset + surface->corner_radius = 0; + surface->blur_enabled = false; + surface->blur_xray = false; + surface->blur_ignore_transparent = false; + surface->shadow_enabled = false; } } -static void arrange_layer(struct sway_output *output, struct wl_list *list, - struct wlr_box *usable_area, bool exclusive) { - struct sway_layer_surface *sway_layer; - struct wlr_box full_area = { 0 }; - wlr_output_effective_resolution(output->wlr_output, - &full_area.width, &full_area.height); - wl_list_for_each(sway_layer, list, link) { - struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer->current; - if (exclusive != (state->exclusive_zone > 0)) { +static void layer_parse_criteria(struct sway_layer_surface *surface) { + if (!surface || !surface->layer_surface + || surface->layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + return; + } + struct layer_criteria *criteria = layer_criteria_for_namespace(surface->layer_surface->namespace); + layer_apply_criteria(surface, criteria); +} + +static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, + struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + // surface could be null during destruction + if (!surface) { continue; } - struct wlr_box bounds; - if (state->exclusive_zone == -1) { - bounds = full_area; - } else { - bounds = *usable_area; - } - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height - }; - // Horizontal axis - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (box.width == 0) { - box.x = bounds.x; - } else if ((state->anchor & both_horiz) == both_horiz) { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - // Vertical axis - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (box.height == 0) { - box.y = bounds.y; - } else if ((state->anchor & both_vert) == both_vert) { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - // Margin - if (box.width == 0) { - box.x += state->margin.left; - box.width = bounds.width - - (state->margin.left + state->margin.right); - } else if ((state->anchor & both_horiz) == both_horiz) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - if (box.height == 0) { - box.y += state->margin.top; - box.height = bounds.height - - (state->margin.top + state->margin.bottom); - } else if ((state->anchor & both_vert) == both_vert) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if (!sway_assert(box.width >= 0 && box.height >= 0, - "Expected layer surface to have positive size")) { + + if (!surface->scene->layer_surface->initialized) { continue; } - // Apply - sway_layer->geo = box; - apply_exclusive(usable_area, state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); - wlr_layer_surface_v1_configure(layer, box.width, box.height); + + if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) { + continue; + } + + wlr_scene_node_set_enabled(&surface->shadow_node->node, surface->shadow_enabled); + if (surface->shadow_enabled) { + // Adjust the size and position of the shadow node + wlr_scene_shadow_set_size(surface->shadow_node, + surface->layer_surface->surface->current.width + config->shadow_blur_sigma * 2, + surface->layer_surface->surface->current.height + config->shadow_blur_sigma * 2); + int x = config->shadow_offset_x - config->shadow_blur_sigma; + int y = config->shadow_offset_y - config->shadow_blur_sigma; + wlr_scene_node_set_position(&surface->shadow_node->node, x, y); + + wlr_scene_shadow_set_clipped_region(surface->shadow_node, (struct clipped_region) { + .corner_radius = surface->corner_radius, + .corners = CORNER_LOCATION_ALL, + .area = { + .x = -x, + .y = -y, + .width = surface->layer_surface->surface->current.width, + .height = surface->layer_surface->surface->current.height, + }, + }); + } + + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); } } @@ -230,48 +128,43 @@ void arrange_layers(struct sway_output *output) { struct wlr_box usable_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height); + const struct wlr_box full_area = usable_area; - // Arrange exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); - if (memcmp(&usable_area, &output->usable_area, - sizeof(struct wlr_box)) != 0) { + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); + + if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + output->usable_area = usable_area; arrange_output(output); + } else { + arrange_popups(root->layers.popup); } - // Arrange non-exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, false); - // Find topmost keyboard interactive layer, if such a layer exists - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, + struct wlr_scene_tree *layers_above_shell[] = { + output->layers.shell_overlay, + output->layers.shell_top, }; size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); - struct sway_layer_surface *layer, *topmost = NULL; + struct wlr_scene_node *node; + struct sway_layer_surface *topmost = NULL; for (size_t i = 0; i < nlayers; ++i) { - wl_list_for_each_reverse(layer, - &output->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive + wl_list_for_each_reverse(node, + &layers_above_shell[i]->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (surface && surface->layer_surface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && - layer->layer_surface->surface->mapped) { - topmost = layer; + surface->layer_surface->surface->mapped) { + topmost = surface; break; } } @@ -293,21 +186,95 @@ void arrange_layers(struct sway_output *output) { } } +static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, + enum zwlr_layer_shell_v1_layer type) { + switch (type) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return output->layers.shell_background; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return output->layers.shell_bottom; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return output->layers.shell_top; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return output->layers.shell_overlay; + } + + sway_assert(false, "unreachable"); + return NULL; +} + +static struct sway_layer_surface *sway_layer_surface_create( + struct wlr_scene_layer_surface_v1 *scene) { + struct sway_layer_surface *surface = calloc(1, sizeof(*surface)); + if (!surface) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); + return NULL; + } + + struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); + if (!popups) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); + free(surface); + return NULL; + } + + surface->desc.relative = &scene->tree->node; + + if (!scene_descriptor_assign(&popups->node, + SWAY_SCENE_DESC_POPUP, &surface->desc)) { + sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); + wlr_scene_node_destroy(&popups->node); + free(surface); + return NULL; + } + + surface->tree = scene->tree; + surface->scene = scene; + surface->layer_surface = scene->layer_surface; + surface->popups = popups; + surface->layer_surface->data = surface; + + // Effects + surface->corner_radius = 0; + surface->blur_enabled = false; + surface->blur_xray = false; + surface->blur_ignore_transparent = false; + surface->shadow_enabled = false; + + bool failed = false; + surface->shadow_node = alloc_scene_shadow(surface->tree, 0, 0, + 0, config->shadow_blur_sigma, config->shadow_color, &failed); + if (failed) { + sway_log(SWAY_ERROR, "Failed to allocate a shadow node"); + wlr_scene_node_destroy(&popups->node); + free(surface); + return NULL; + } + + return surface; +} + static struct sway_layer_surface *find_mapped_layer_by_client( - struct wl_client *client, struct wlr_output *ignore_output) { + struct wl_client *client, struct sway_output *ignore_output) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - if (output->wlr_output == ignore_output) { + if (output == ignore_output) { continue; } // For now we'll only check the overlay layer - struct sway_layer_surface *lsurface; - wl_list_for_each(lsurface, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wl_resource *resource = lsurface->layer_surface->resource; + struct wlr_scene_node *node; + wl_list_for_each (node, &output->layers.shell_overlay->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (!surface) { + continue; + } + + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + struct wl_resource *resource = layer_surface->resource; if (wl_resource_get_client(resource) == client - && lsurface->layer_surface->surface->mapped) { - return lsurface; + && layer_surface->surface->mapped) { + return surface; } } } @@ -315,317 +282,165 @@ static struct sway_layer_surface *find_mapped_layer_by_client( } static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, output_destroy); + struct sway_layer_surface *layer = + wl_container_of(listener, layer, output_destroy); + + layer->output = NULL; + wlr_scene_node_destroy(&layer->scene->tree->node); +} + +static void handle_node_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *layer = + wl_container_of(listener, layer, node_destroy); + + // destroy the scene descriptor straight away if it exists, otherwise + // we will try to reflow still considering the destroyed node. + scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL); + // Determine if this layer is being used by an exclusive client. If it is, // try and find another layer owned by this client to pass focus to. struct sway_seat *seat = input_manager_get_default_seat(); struct wl_client *client = - wl_resource_get_client(sway_layer->layer_surface->resource); - bool set_focus = seat->exclusive_client == client; - - if (set_focus) { - struct sway_layer_surface *layer = - find_mapped_layer_by_client(client, sway_layer->layer_surface->output); - if (layer) { - seat_set_focus_layer(seat, layer->layer_surface); + wl_resource_get_client(layer->layer_surface->resource); + if (!server.session_lock.lock) { + struct sway_layer_surface *consider_layer = + find_mapped_layer_by_client(client, layer->output); + if (consider_layer) { + seat_set_focus_layer(seat, consider_layer->layer_surface); } } - wlr_layer_surface_v1_destroy(sway_layer->layer_surface); + struct wlr_layer_surface_v1 *wlr_layer_surface = layer->layer_surface; + if (wlr_layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + if (layer->output) { + wlr_scene_optimized_blur_mark_dirty(layer->output->layers.blur_layer); + } + } + + if (layer->output) { + arrange_layers(layer->output); + transaction_commit_dirty(); + } + + wlr_scene_node_destroy(&layer->popups->node); + + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->output_destroy.link); + + layer->layer_surface->data = NULL; + + free(layer); } static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_layer_surface *layer = - wl_container_of(listener, layer, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_output *wlr_output = layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - struct wlr_box old_extent = layer->extent; + struct sway_layer_surface *surface = + wl_container_of(listener, surface, surface_commit); - // Rerender the static blur on change - if (layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND - || layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + if (!layer_surface->initialized) { + return; } - bool layer_changed = false; - if (layer_surface->current.committed != 0 - || layer->mapped != layer_surface->surface->mapped) { - layer->mapped = layer_surface->surface->mapped; - layer_changed = layer->layer != layer_surface->current.layer; - if (layer_changed) { - wl_list_remove(&layer->link); - wl_list_insert(&output->layers[layer_surface->current.layer], - &layer->link); - layer->layer = layer_surface->current.layer; - layer_parse_criteria(layer); + // Rerender the optimized blur on change + struct wlr_layer_surface_v1 *wlr_layer_surface = surface->layer_surface; + if (wlr_layer_surface->current.layer == + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + if (surface->output) { + wlr_scene_optimized_blur_mark_dirty(surface->output->layers.blur_layer); } - arrange_layers(output); } - wlr_surface_get_extends(layer_surface->surface, &layer->extent); - layer->extent.x += layer->geo.x; - layer->extent.y += layer->geo.y; - - bool extent_changed = - memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; - if (extent_changed || layer_changed) { - int blur_size = layer->has_blur? config_get_blur_size(): 0; - int shadow_sigma = layer->has_shadow? config->shadow_blur_sigma: 0; - int effect_size = MAX(blur_size, shadow_sigma); - old_extent.x += output->lx - effect_size; - old_extent.y += output->ly - effect_size; - old_extent.width += effect_size * 2; - old_extent.height += effect_size * 2; - output_damage_box(output, &old_extent); - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, true); - } else { - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, false); + uint32_t committed = layer_surface->current.committed; + if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + surface->output, layer_type); + wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); } - transaction_commit_dirty(); + if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { + surface->mapped = layer_surface->surface->mapped; + layer_parse_criteria(surface); + arrange_layers(surface->output); + transaction_commit_dirty(); + } } -static void unmap(struct sway_layer_surface *sway_layer) { +static void handle_map(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of(listener, + surface, map); + + struct wlr_layer_surface_v1 *layer_surface = + surface->scene->layer_surface; + + layer_parse_criteria(surface); + wlr_scene_node_lower_to_bottom(&surface->shadow_node->node); + + // focus on new surface + if (layer_surface->current.keyboard_interactive && + (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || + layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + // but only if the currently focused layer has a lower precedence + if (!seat->focused_layer || + seat->focused_layer->current.layer >= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } + arrange_layers(surface->output); + } + + cursor_rebase_all(); +} + +static void handle_unmap(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of( + listener, surface, unmap); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (seat->focused_layer == sway_layer->layer_surface) { + if (seat->focused_layer == surface->layer_surface) { seat_set_focus_layer(seat, NULL); } } cursor_rebase_all(); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, destroy); - sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", - sway_layer->layer_surface->namespace); - if (sway_layer->layer_surface->surface->mapped) { - unmap(sway_layer); - } - - struct sway_layer_subsurface *subsurface, *subsurface_tmp; - wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { - layer_subsurface_destroy(subsurface); - } - - wl_list_remove(&sway_layer->link); - wl_list_remove(&sway_layer->destroy.link); - wl_list_remove(&sway_layer->map.link); - wl_list_remove(&sway_layer->unmap.link); - wl_list_remove(&sway_layer->surface_commit.link); - wl_list_remove(&sway_layer->new_popup.link); - wl_list_remove(&sway_layer->new_subsurface.link); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - - // Rerender the static blur - if (sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND - || sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - } - - arrange_layers(output); - transaction_commit_dirty(); - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - - free(sway_layer); -} - -static void handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of(listener, - sway_layer, map); - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - layer_parse_criteria(sway_layer); - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); - cursor_rebase_all(); -} - -static void handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of( - listener, sway_layer, unmap); - unmap(sway_layer); -} - -static void subsurface_damage(struct sway_layer_subsurface *subsurface, - bool whole) { - struct sway_layer_surface *layer = subsurface->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; - int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; - output_damage_surface( - output, ox, oy, subsurface->wlr_subsurface->surface, whole); -} - -static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, unmap); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, map); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, commit); - subsurface_damage(subsurface, false); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { - wl_list_remove(&subsurface->link); - wl_list_remove(&subsurface->map.link); - wl_list_remove(&subsurface->unmap.link); - wl_list_remove(&subsurface->destroy.link); - wl_list_remove(&subsurface->commit.link); - free(subsurface); -} - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - layer_subsurface_destroy(subsurface); -} - -static struct sway_layer_subsurface *create_subsurface( - struct wlr_subsurface *wlr_subsurface, - struct sway_layer_surface *layer_surface) { - struct sway_layer_subsurface *subsurface = - calloc(1, sizeof(struct sway_layer_subsurface)); - if (subsurface == NULL) { - return NULL; - } - - subsurface->wlr_subsurface = wlr_subsurface; - subsurface->layer_surface = layer_surface; - wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); - - subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->surface->events.map, &subsurface->map); - subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->surface->events.unmap, &subsurface->unmap); - subsurface->destroy.notify = subsurface_handle_destroy; - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->commit.notify = subsurface_handle_commit; - wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); - - return subsurface; -} - -static void handle_new_subsurface(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer_surface = - wl_container_of(listener, sway_layer_surface, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - create_subsurface(wlr_subsurface, sway_layer_surface); -} - - -static struct sway_layer_surface *popup_get_layer( - struct sway_layer_popup *popup) { - while (popup->parent_type == LAYER_PARENT_POPUP) { - popup = popup->parent_popup; - } - return popup->parent_layer; -} - -static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { - struct wlr_xdg_popup *popup = layer_popup->wlr_popup; - struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; - int ox = popup_sx, oy = popup_sy; - struct sway_layer_surface *layer; - while (true) { - if (layer_popup->parent_type == LAYER_PARENT_POPUP) { - layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->current.geometry.x; - oy += layer_popup->wlr_popup->current.geometry.y; - } else { - layer = layer_popup->parent_layer; - ox += layer->geo.x; - oy += layer->geo.y; - break; - } - } - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, ox, oy, surface, whole); -} - -static void popup_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, map); - struct sway_layer_surface *layer = popup_get_layer(popup); - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - surface_enter_output(popup->wlr_popup->base->surface, wlr_output->data); - popup_damage(popup, true); -} - -static void popup_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); - popup_damage(popup, true); -} - -static void popup_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); - popup_damage(popup, false); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_popup *popup = wl_container_of(listener, popup, destroy); - wl_list_remove(&popup->map.link); - wl_list_remove(&popup->unmap.link); wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->commit.link); free(popup); } static void popup_unconstrain(struct sway_layer_popup *popup) { - struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct sway_output *output = popup->toplevel->output; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; + // if a client tries to create a popup while we are in the process of destroying + // its output, don't crash. + if (!output) { + return; + } + + int lx, ly; + wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = -layer->geo.x, - .y = -layer->geo.y, + .x = output->lx - lx, + .y = output->ly - ly, .width = output->width, .height = output->height, }; @@ -633,32 +448,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } +static void popup_handle_commit(struct wl_listener *listener, void *data) { + struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); + if (popup->wlr_popup->base->initial_commit) { + popup_unconstrain(popup); + } +} + static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, - enum layer_parent parent_type, void *parent) { - struct sway_layer_popup *popup = - calloc(1, sizeof(struct sway_layer_popup)); + struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { + struct sway_layer_popup *popup = calloc(1, sizeof(*popup)); if (popup == NULL) { return NULL; } + popup->toplevel = toplevel; popup->wlr_popup = wlr_popup; - popup->parent_type = parent_type; - popup->parent_layer = parent; + popup->scene = wlr_scene_xdg_surface_create(parent, + wlr_popup->base); + + if (!popup->scene) { + free(popup); + return NULL; + } - popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->surface->events.map, &popup->map); - popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->surface->events.unmap, &popup->unmap); popup->destroy.notify = popup_handle_destroy; wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - popup->commit.notify = popup_handle_commit; - wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); - - popup_unconstrain(popup); + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); return popup; } @@ -667,19 +488,14 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_popup *sway_layer_popup = wl_container_of(listener, sway_layer_popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); + create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); } static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer_surface = wl_container_of(listener, sway_layer_surface, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); -} - -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface) { - return layer_surface->data; + create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); } void handle_layer_shell_surface(struct wl_listener *listener, void *data) { @@ -711,10 +527,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); - // Note that layer_surface->output can be NULL - // here, but none of our destroy callbacks are - // registered yet so we don't have to make them - // handle that case. wlr_layer_surface_v1_destroy(layer_surface); return; } @@ -723,51 +535,57 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->wlr_output; } - struct sway_layer_surface *sway_layer = - calloc(1, sizeof(struct sway_layer_surface)); - if (!sway_layer) { + struct sway_output *output = layer_surface->output->data; + + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + output, layer_type); + struct wlr_scene_layer_surface_v1 *scene_surface = + wlr_scene_layer_surface_v1_create(output_layer, layer_surface); + if (!scene_surface) { + sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); return; } - wl_list_init(&sway_layer->subsurfaces); + struct sway_layer_surface *surface = + sway_layer_surface_create(scene_surface); + if (!surface) { + wlr_layer_surface_v1_destroy(layer_surface); - sway_layer->surface_commit.notify = handle_surface_commit; + sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); + return; + } + + if (!scene_descriptor_assign(&scene_surface->tree->node, + SWAY_SCENE_DESC_LAYER_SHELL, surface)) { + sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor"); + // destroying the layer_surface will also destroy its corresponding + // scene node + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + + surface->output = output; + + // now that the surface's output is known, we can advertise its scale + wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, + layer_surface->output->scale); + wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface, + ceil(layer_surface->output->scale)); + + surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, - &sway_layer->surface_commit); + &surface->surface_commit); + surface->map.notify = handle_map; + wl_signal_add(&layer_surface->surface->events.map, &surface->map); + surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); + surface->new_popup.notify = handle_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); - sway_layer->destroy.notify = handle_destroy; - wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); - sway_layer->map.notify = handle_map; - wl_signal_add(&layer_surface->surface->events.map, &sway_layer->map); - sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->surface->events.unmap, &sway_layer->unmap); - sway_layer->new_popup.notify = handle_new_popup; - wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); - sway_layer->new_subsurface.notify = handle_new_subsurface; - wl_signal_add(&layer_surface->surface->events.new_subsurface, - &sway_layer->new_subsurface); + surface->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.disable, &surface->output_destroy); - sway_layer->layer_surface = layer_surface; - layer_surface->data = sway_layer; - - sway_layer->has_blur = false; - sway_layer->blur_ignore_transparent = false; - sway_layer->has_shadow = false; - sway_layer->corner_radius = 0; - - struct sway_output *output = layer_surface->output->data; - sway_layer->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.disable, &sway_layer->output_destroy); - - wl_list_insert(&output->layers[layer_surface->pending.layer], - &sway_layer->link); - - surface_enter_output(layer_surface->surface, output); - - // Temporarily set the layer's current state to pending - // So that we can easily arrange it - struct wlr_layer_surface_v1_state old_state = layer_surface->current; - layer_surface->current = layer_surface->pending; - arrange_layers(output); - layer_surface->current = old_state; + surface->node_destroy.notify = handle_node_destroy; + wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3e6b1a2d..8a998067 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1,7 +1,4 @@ -#define _POSIX_C_SOURCE 200809L #include -#include -#include #include #include #include @@ -9,30 +6,32 @@ #include #include #include -#include -#include #include +#include #include #include #include +#include +#include #include #include #include -#include +#include +#include #include +#include #include "config.h" #include "log.h" -#include "scenefx/render/pass.h" +#include "scenefx/types/fx/corner_location.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" -#include "sway/input/text_input.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -76,354 +75,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) { return NULL; } -struct surface_iterator_data { - sway_surface_iterator_func_t user_iterator; - void *user_data; - - struct sway_output *output; - struct sway_view *view; - double ox, oy; - int width, height; -}; - -static bool get_surface_box(struct surface_iterator_data *data, - struct wlr_surface *surface, int sx, int sy, - struct wlr_box *surface_box) { - struct sway_output *output = data->output; - - if (!wlr_surface_has_buffer(surface)) { - return false; - } - - int sw = surface->current.width; - int sh = surface->current.height; - - struct wlr_box box = { - .x = floor(data->ox + sx), - .y = floor(data->oy + sy), - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&intersection, &output_box, &box); -} - -static void output_for_each_surface_iterator(struct wlr_surface *surface, - int sx, int sy, void *_data) { - struct surface_iterator_data *data = _data; - - struct wlr_box box; - bool intersects = get_surface_box(data, surface, sx, sy, &box); - if (!intersects) { - return; - } - - data->user_iterator(data->output, data->view, surface, &box, - data->user_data); -} - -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = ox, - .oy = oy, - .width = surface->current.width, - .height = surface->current.height, - }; - - wlr_surface_for_each_surface(surface, - output_for_each_surface_iterator, &data); -} - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_surface(view, output_for_each_surface_iterator, &data); -} - -void output_view_for_each_popup_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); -} - -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - - struct render_data *data = user_data; - data->deco_data.blur = layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ? - layer_surface->has_blur : false; - data->deco_data.discard_transparent = layer_surface->blur_ignore_transparent; - data->deco_data.shadow = layer_surface->has_shadow; - data->deco_data.corner_radius = layer_surface->corner_radius; - - output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, - layer_surface->geo.x, layer_surface->geo.y, iterator, - user_data); - } -} - - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->lx; - double oy = unmanaged_surface->ly - output->ly; - - output_surface_for_each_surface(output, xsurface->surface, ox, oy, - iterator, user_data); - } -} -#endif - -void output_input_popups_for_each_surface(struct sway_output *output, - struct wl_list *input_popups, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_input_popup *popup; - wl_list_for_each(popup, input_popups, link) { - int lx, ly; - if (!sway_input_popup_get_position(popup, &lx, &ly)) { - continue; - } - if (!popup->popup_surface->surface->mapped || !popup->visible) { - continue; - } - - // damage the popup if surface is updated - sway_input_popup_damage(popup); - - double ox = lx - output->lx; - double oy = ly - output->ly; - - output_surface_for_each_surface(output, - popup->popup_surface->surface, ox, oy, - iterator, user_data); - } -} - -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->lx; - double oy = drag_icon->y - output->ly; - - if (drag_icon->wlr_drag_icon->surface->mapped) { - output_surface_for_each_surface(output, - drag_icon->wlr_drag_icon->surface, ox, oy, - iterator, user_data); - } - } -} - -static void for_each_surface_container_iterator(struct sway_container *con, - void *_data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - - struct surface_iterator_data *data = _data; - output_view_for_each_surface(data->output, con->view, - data->user_iterator, data->user_data); -} - -static void output_for_each_surface(struct sway_output *output, - sway_surface_iterator_func_t iterator, void *user_data) { - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return; - } - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != output->wlr_output) { - continue; - } - if (!lock_surface->surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, iterator, user_data); - } - return; - } - - if (output_has_opaque_overlay_layer_surface(output)) { - goto overlay; - } - - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - }; - - struct sway_workspace *workspace = output_get_active_workspace(output); - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - if (!workspace) { - return; - } - fullscreen_con = workspace->current.fullscreen; - } - if (fullscreen_con) { - for_each_surface_container_iterator(fullscreen_con, &data); - container_for_each_child(fullscreen_con, - for_each_surface_container_iterator, &data); - - // TODO: Show transient containers for fullscreen global - if (fullscreen_con == workspace->current.fullscreen) { - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - for_each_surface_container_iterator(floater, &data); - } - } - } -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - } else { - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - iterator, user_data); - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - iterator, user_data); - - workspace_for_each_container(workspace, - for_each_surface_container_iterator, &data); - -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - iterator, user_data); - } - -overlay: - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - iterator, user_data); - output_input_popups_for_each_surface( - output, &input_manager_current_seat()->im_relay.input_popups, - iterator, user_data); - output_drag_icons_for_each_surface(output, &root->drag_icons, - iterator, user_data); -} - -static int scale_length(int length, int offset, float scale) { - return roundf((offset + length) * scale) - roundf(offset * scale); -} - -void scale_box(struct wlr_box *box, float scale) { - box->width = scale_length(box->width, box->x, scale); - box->height = scale_length(box->height, box->y, scale); - box->x = roundf(box->x * scale); - box->y = roundf(box->y * scale); -} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); @@ -437,310 +88,278 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { return focus->sway_workspace; } -bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { - struct sway_layer_surface *sway_layer_surface; - wl_list_for_each(sway_layer_surface, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; - pixman_box32_t output_box = { - .x2 = output->width, - .y2 = output->height, - }; - pixman_region32_t surface_opaque_box; - pixman_region32_init(&surface_opaque_box); - pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); - pixman_region32_translate(&surface_opaque_box, - sway_layer_surface->geo.x, sway_layer_surface->geo.y); - pixman_region_overlap_t contains = - pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); - pixman_region32_fini(&surface_opaque_box); - - if (contains == PIXMAN_REGION_IN) { - return true; - } - } - return false; -} - struct send_frame_done_data { struct timespec when; int msec_until_refresh; + struct sway_output *output; }; -static void send_frame_done_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) { - int view_max_render_time = 0; - if (view != NULL) { - view_max_render_time = view->max_render_time; +struct buffer_timer { + struct wl_listener destroy; + struct wl_event_source *frame_done_timer; +}; + +static int handle_buffer_timer(void *data) { + struct wlr_scene_buffer *buffer = data; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_buffer_send_frame_done(buffer, &now); + return 0; +} + +static void handle_buffer_timer_destroy(struct wl_listener *listener, + void *data) { + struct buffer_timer *timer = wl_container_of(listener, timer, destroy); + + wl_list_remove(&timer->destroy.link); + wl_event_source_remove(timer->frame_done_timer); + free(timer); +} + +static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { + struct buffer_timer *timer = + scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); + if (timer) { + return timer; } + timer = calloc(1, sizeof(struct buffer_timer)); + if (!timer) { + return NULL; + } + + timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_buffer_timer, buffer); + if (!timer->frame_done_timer) { + free(timer); + return NULL; + } + + scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); + + timer->destroy.notify = handle_buffer_timer_destroy; + wl_signal_add(&buffer->node.events.destroy, &timer->destroy); + + return timer; +} + +static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, + int x, int y, void *user_data) { struct send_frame_done_data *data = user_data; + struct sway_output *output = data->output; + int view_max_render_time = 0; + + if (buffer->primary_output != data->output->scene_output) { + return; + } + + struct wlr_scene_node *current = &buffer->node; + while (true) { + struct sway_view *view = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_VIEW); + if (view) { + view_max_render_time = view->max_render_time; + break; + } + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } int delay = data->msec_until_refresh - output->max_render_time - view_max_render_time; - if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { - wlr_surface_send_frame_done(surface, &data->when); + struct buffer_timer *timer = NULL; + + if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { + timer = buffer_timer_get_or_create(buffer); + } + + if (timer) { + wl_event_source_timer_update(timer->frame_done_timer, delay); } else { - struct sway_surface *sway_surface = surface->data; - wl_event_source_timer_update(sway_surface->frame_done_timer, delay); + wlr_scene_buffer_send_frame_done(buffer, &data->when); } } -static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { - output_for_each_surface(output, send_frame_done_iterator, data); +static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, + struct wlr_scene_buffer *buffer) { + // if we are scaling down, we should always choose linear + if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( + buffer->dst_width < buffer->buffer_width || + buffer->dst_height < buffer->buffer_height)) { + return WLR_SCALE_FILTER_BILINEAR; + } + + switch (output->scale_filter) { + case SCALE_FILTER_LINEAR: + return WLR_SCALE_FILTER_BILINEAR; + case SCALE_FILTER_NEAREST: + return WLR_SCALE_FILTER_NEAREST; + default: + abort(); // unreachable + } } -static void count_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *data) { - size_t *n = data; - (*n)++; -} - -static bool scan_out_fullscreen_view(struct sway_output *output, - struct wlr_output_state *pending, struct sway_view *view) { - struct wlr_output *wlr_output = output->wlr_output; - struct sway_workspace *workspace = output->current.active_workspace; - if (!sway_assert(workspace, "Expected an active workspace")) { - return false; +static void output_configure_scene(struct sway_output *output, struct wlr_scene_node *node, float opacity, + int corner_radius, bool blur_enabled, bool has_titlebar, struct sway_container *closest_con) { + if (!node->enabled) { + return; } - if (server.session_lock.locked) { - return false; + struct sway_container *con = + scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER); + if (con) { + closest_con = con; + opacity = con->alpha; + corner_radius = con->corner_radius; + blur_enabled = con->blur_enabled; + + enum sway_container_layout layout = con->current.layout; + has_titlebar |= con->current.border == B_NORMAL || layout == L_STACKED || layout == L_TABBED; } - if (!wl_list_empty(&view->saved_buffers)) { - return false; - } + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer); - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, view->container)) { - return false; + if (surface) { + const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state = + wlr_alpha_modifier_v1_get_surface_state(surface->surface); + if (alpha_modifier_state != NULL) { + opacity *= (float)alpha_modifier_state->multiplier; + } + } + + // hack: don't call the scene setter because that will damage all outputs + // We don't want to damage outputs that aren't our current output that + // we're configuring + buffer->filter_mode = get_scale_filter(output, buffer); + + wlr_scene_buffer_set_opacity(buffer, opacity); + + if (!surface || !surface->surface) { + return; + } + // Other buffers should set their own effects manually, like the + // text buffer and saved views + struct wlr_layer_surface_v1 *layer_surface = NULL; + if (wlr_xdg_surface_try_from_wlr_surface(surface->surface) + || wlr_xwayland_surface_try_from_wlr_surface(surface->surface)) { + wlr_scene_buffer_set_corner_radius(buffer, + container_has_corner_radius(closest_con) ? corner_radius : 0, + has_titlebar ? CORNER_LOCATION_BOTTOM : CORNER_LOCATION_ALL); + wlr_scene_buffer_set_backdrop_blur(buffer, blur_enabled); + wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, false); + // Only enable xray blur if tiled or when xray is explicitly enabled + bool should_optimize_blur = (closest_con && !container_is_floating_or_child(closest_con)) || config->blur_xray; + wlr_scene_buffer_set_backdrop_blur_optimized(buffer, should_optimize_blur); + } else if (wlr_subsurface_try_from_wlr_surface(surface->surface)) { + wlr_scene_buffer_set_corner_radius(buffer, + container_has_corner_radius(closest_con) ? corner_radius : 0, + CORNER_LOCATION_ALL); + } else if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(surface->surface)) + && layer_surface->data) { + // Layer effects + struct sway_layer_surface *surface = layer_surface->data; + wlr_scene_buffer_set_corner_radius(buffer, surface->corner_radius, CORNER_LOCATION_ALL); + wlr_scene_shadow_set_blur_sigma(surface->shadow_node, config->shadow_blur_sigma); + wlr_scene_shadow_set_corner_radius(surface->shadow_node, surface->corner_radius); + wlr_scene_buffer_set_backdrop_blur(buffer, surface->blur_enabled); + wlr_scene_buffer_set_backdrop_blur_ignore_transparent(buffer, surface->blur_ignore_transparent); + wlr_scene_buffer_set_backdrop_blur_optimized(buffer, surface->blur_xray); + } + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + output_configure_scene(output, node, opacity, corner_radius, blur_enabled, has_titlebar, closest_con); } } - -#if HAVE_XWAYLAND - if (!wl_list_empty(&root->xwayland_unmanaged)) { - return false; - } -#endif - - if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { - return false; - } - if (!wl_list_empty(&root->drag_icons)) { - return false; - } - - struct wlr_surface *surface = view->surface; - if (surface == NULL) { - return false; - } - size_t n_surfaces = 0; - output_view_for_each_surface(output, view, - count_surface_iterator, &n_surfaces); - if (n_surfaces != 1) { - return false; - } - size_t n_popups = 0; - output_view_for_each_popup_surface(output, view, - count_surface_iterator, &n_popups); - if (n_popups > 0) { - return false; - } - - if (surface->buffer == NULL) { - return false; - } - - if ((float)surface->current.scale != wlr_output->scale || - surface->current.transform != wlr_output->transform) { - return false; - } - - if (!wlr_output_is_direct_scanout_allowed(wlr_output)) { - return false; - } - - wlr_output_state_set_buffer(pending, &surface->buffer->base); - - if (!wlr_output_test_state(wlr_output, pending)) { - return false; - } - - wlr_presentation_surface_scanned_out_on_output(server.presentation, surface, - wlr_output); - - return wlr_output_commit_state(wlr_output, pending); } -static void get_frame_damage(struct sway_output *output, - pixman_region32_t *frame_damage) { - struct wlr_output *wlr_output = output->wlr_output; - - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - - pixman_region32_init(frame_damage); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(frame_damage, &output->damage_ring.current, - transform, width, height); - - if (debug.damage != DAMAGE_DEFAULT) { - pixman_region32_union_rect(frame_damage, frame_damage, - 0, 0, wlr_output->width, wlr_output->height); - } -} - -static int output_repaint_timer_handler(void *data) { - struct sway_output *output = data; - struct wlr_output *wlr_output = output->wlr_output; - if (wlr_output == NULL) { - return 0; - } - - wlr_output->frame_pending = false; - - if (!wlr_output->needs_frame && - !output->gamma_lut_changed && - !pixman_region32_not_empty(&output->damage_ring.current)) { - return 0; - } - +static bool output_can_tear(struct sway_output *output) { struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return 0; + if (!workspace) { + return false; } struct sway_container *fullscreen_con = root->fullscreen_global; if (!fullscreen_con) { fullscreen_con = workspace->current.fullscreen; } + if (fullscreen_con && fullscreen_con->view) { + return (output->allow_tearing && view_can_tear(fullscreen_con->view)); + } - struct wlr_output_state pending = {0}; + return false; +} + +static int output_repaint_timer_handler(void *data) { + struct sway_output *output = data; + + output->wlr_output->frame_pending = false; + if (!output->enabled) { + return 0; + } + output_configure_scene(output, &root->root_scene->tree.node, 1.0f, + 0, false, false, NULL); + + struct wlr_scene_output_state_options opts = { + .color_transform = output->color_transform, + }; + + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_scene_output *scene_output = output->scene_output; + if (!wlr_output->needs_frame && !output->gamma_lut_changed && + !pixman_region32_not_empty(&scene_output->pending_commit_damage)) { + return 0; + } + + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { + wlr_output_state_finish(&pending); + 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( - server.gamma_control_manager_v1, wlr_output); + server.gamma_control_manager_v1, output->wlr_output); if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - goto out; - } - if (!wlr_output_test_state(wlr_output, &pending)) { wlr_output_state_finish(&pending); - pending = (struct wlr_output_state){0}; + return 0; + } + + if (!wlr_output_test_state(output->wlr_output, &pending)) { wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } } - pending.committed |= WLR_OUTPUT_STATE_DAMAGE; - get_frame_damage(output, &pending.damage); + if (output_can_tear(output)) { + pending.tearing_page_flip = true; - if (fullscreen_con && fullscreen_con->view && !debug.noscanout - // Only output to monitor without compositing when saturation is changed - && fullscreen_con->saturation == 1.0f) { - // Try to scan-out the fullscreen view - static bool last_scanned_out = false; - bool scanned_out = - scan_out_fullscreen_view(output, &pending, fullscreen_con->view); - - if (scanned_out && !last_scanned_out) { - sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", + if (!wlr_output_test_state(output->wlr_output, &pending)) { + sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", output->wlr_output->name); - } - if (last_scanned_out && !scanned_out) { - sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", - output->wlr_output->name); - output_damage_whole(output); - } - last_scanned_out = scanned_out; - - if (scanned_out) { - goto out; + pending.tearing_page_flip = false; } } - if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) { - goto out; + if (!wlr_output_commit_state(output->wlr_output, &pending)) { + sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); } - - int buffer_age; - struct wlr_buffer *buffer = wlr_swapchain_acquire(wlr_output->swapchain, &buffer_age); - if (buffer == NULL) { - goto out; - } - - struct fx_gles_render_pass *render_pass = fx_renderer_begin_buffer_pass( - wlr_output->renderer, buffer, wlr_output, - &(struct wlr_buffer_pass_options) { - .timer = NULL, - } - ); - if (render_pass == NULL) { - wlr_buffer_unlock(buffer); - goto out; - } - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage); - - if (debug.damage == DAMAGE_RERENDER) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); - } - - struct fx_render_context ctx = { - .output_damage = &damage, - .renderer = wlr_output->renderer, - .output = output, - .pass = render_pass, - }; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - output_render(&ctx); - - pixman_region32_fini(&damage); - - if (!wlr_render_pass_submit(&render_pass->base)) { - wlr_buffer_unlock(buffer); - goto out; - } - - wlr_output_state_set_buffer(&pending, buffer); - wlr_buffer_unlock(buffer); - - if (!wlr_output_commit_state(wlr_output, &pending)) { - goto out; - } - - wlr_damage_ring_rotate(&output->damage_ring); - output->last_frame = now; - -out: wlr_output_state_finish(&pending); return 0; } -static void handle_damage(struct wl_listener *listener, void *user_data) { - struct sway_output *output = - wl_container_of(listener, output, damage); - struct wlr_output_event_damage *event = user_data; - if (wlr_damage_ring_add(&output->damage_ring, event->damage)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - static void handle_frame(struct wl_listener *listener, void *user_data) { struct sway_output *output = wl_container_of(listener, output, frame); @@ -801,136 +420,11 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { struct send_frame_done_data data = {0}; clock_gettime(CLOCK_MONOTONIC, &data.when); data.msec_until_refresh = msec_until_refresh; - send_frame_done(output, &data); + data.output = output; + wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } -static void handle_needs_frame(struct wl_listener *listener, void *user_data) { - struct sway_output *output = - wl_container_of(listener, output, needs_frame); - wlr_output_schedule_frame(output->wlr_output); -} - -void output_damage_whole(struct sway_output *output) { - // The output can exist with no wlr_output if it's just been disconnected - // and the transaction to evacuate it has't completed yet. - if (output != NULL && output->wlr_output != NULL) { - wlr_damage_ring_add_whole(&output->damage_ring); - wlr_output_schedule_frame(output->wlr_output); - } -} - -static void damage_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - bool *data = _data; - bool whole = *data; - - struct wlr_box box = *_box; - scale_box(&box, output->wlr_output->scale); - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceilf(output->wlr_output->scale) > surface->current.scale) { - // When scaling up a surface, it'll become blurry so we need to - // expand the damage region - wlr_region_expand(&damage, &damage, - ceilf(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - if (wlr_damage_ring_add(&output->damage_ring, &damage)) { - wlr_output_schedule_frame(output->wlr_output); - } - pixman_region32_fini(&damage); - - if (whole) { - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } - } - - if (!wl_list_empty(&surface->current.frame_callback_list)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole) { - output_surface_for_each_surface(output, surface, ox, oy, - damage_surface_iterator, &whole); -} - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - if (!view_is_visible(view)) { - return; - } - bool whole = false; - output_view_for_each_surface(output, view, damage_surface_iterator, &whole); -} - -// Expecting an unscaled box in layout coordinates -void output_damage_box(struct sway_output *output, struct wlr_box *_box) { - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx; - box.y -= output->ly; - scale_box(&box, output->wlr_output->scale); - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - -static void damage_child_views_iterator(struct sway_container *con, - void *data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - struct sway_output *output = data; - bool whole = true; - output_view_for_each_surface(output, con->view, damage_surface_iterator, - &whole); -} - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con) { - // Pad the box by 1px, because the width is a double and might be a fraction - struct wlr_box box = { - .x = con->current.x - output->lx - 1, - .y = con->current.y - output->ly - 1, - .width = con->current.width + 2, - .height = con->current.height + 2, - }; - scale_box(&box, output->wlr_output->scale); - if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { - wlr_output_schedule_frame(output->wlr_output); - } - - // Shadow damage - if (con->shadow_enabled && config_should_parameters_shadow()) { - const int shadow_sigma = config->shadow_blur_sigma; - struct wlr_box shadow_box = { - .x = con->current.x - output->lx - 1 - shadow_sigma + config->shadow_offset_x, - .y = con->current.y - output->ly - 1 - shadow_sigma + config->shadow_offset_y, - .width = con->current.width + 2 + (shadow_sigma * 2), - .height = con->current.height + 2 + (shadow_sigma * 2), - }; - scale_box(&shadow_box, output->wlr_output->scale); - if (wlr_damage_ring_add_box(&output->damage_ring, &shadow_box)) { - wlr_output_schedule_frame(output->wlr_output); - } - } - - // Damage subsurfaces as well, which may extend outside the box - if (con->view) { - damage_child_views_iterator(con, output); - } else { - container_for_each_child(con, damage_child_views_iterator, output); - } -} - -static void update_output_manager_config(struct sway_server *server) { +void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -955,9 +449,36 @@ static void update_output_manager_config(struct sway_server *server) { ipc_event_output(); } -static void begin_destroy(struct sway_output *output) { - struct sway_server *server = output->server; +static int timer_modeset_handle(void *data) { + struct sway_server *server = data; + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + apply_stored_output_configs(); + return 0; +} + +void request_modeset(void) { + 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); + } +} + +bool modeset_is_pending(void) { + return server.delayed_modeset != NULL; +} + +void force_modeset(void) { + if (server.delayed_modeset != NULL) { + wl_event_source_remove(server.delayed_modeset); + server.delayed_modeset = NULL; + } + apply_stored_output_configs(); +} + +static void begin_destroy(struct sway_output *output) { if (output->enabled) { output_disable(output); } @@ -970,19 +491,18 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->present.link); - wl_list_remove(&output->damage.link); wl_list_remove(&output->frame.link); - wl_list_remove(&output->needs_frame.link); wl_list_remove(&output->request_state.link); - wlr_damage_ring_finish(&output->damage_ring); - + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; output->wlr_output->data = NULL; output->wlr_output = NULL; - transaction_commit_dirty(); + wl_event_source_remove(output->repaint_timer); + output->repaint_timer = NULL; - update_output_manager_config(server); + request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -995,38 +515,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) { begin_destroy(output); } -static void update_textures(struct sway_container *con, void *data) { - container_update_title_textures(con); - container_update_marks_textures(con); -} - -static void update_output_scale_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) { - surface_update_outputs(surface); -} - -static void update_im_scale(struct sway_output *output) { - struct sway_seat* im_seat = input_manager_current_seat(); - if (im_seat == NULL) { - return; - } - struct sway_input_method_relay* relay = &im_seat->im_relay; - struct sway_input_popup *popup; - wl_list_for_each(popup, &relay->input_popups, link) { - struct wl_list current_outputs = popup->popup_surface->surface->current_outputs; - struct wlr_surface_output *current_output; - wl_list_for_each(current_output, ¤t_outputs, link) { - if (current_output->output == output->wlr_output) { - double scale = current_output->output->scale; - wlr_fractional_scale_v1_notify_scale(popup->popup_surface->surface, scale); - wlr_surface_set_preferred_buffer_scale(popup->popup_surface->surface, ceil(scale)); - break; - } - } - } -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; @@ -1035,37 +523,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->state->committed & WLR_OUTPUT_STATE_SCALE) { - output_for_each_container(output, update_textures, NULL); - output_for_each_surface(output, update_output_scale_iterator, NULL); - update_im_scale(output); - } - - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE)) { - // Mark optimized blur as dirty - struct fx_effect_framebuffers *effect_fbos = - fx_effect_framebuffers_try_get(output->wlr_output); - effect_fbos->blur_buffer_dirty = true; - - arrange_layers(output); - arrange_output(output); - transaction_commit_dirty(); - - update_output_manager_config(output->server); - } - - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM)) { - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - wlr_damage_ring_set_bounds(&output->damage_ring, width, height); - wlr_output_schedule_frame(output->wlr_output); - } - // Next time the output is enabled, try to re-apply the gamma LUT if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { output->gamma_lut_changed = true; @@ -1088,7 +545,44 @@ static void handle_request_state(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; - wlr_output_commit_state(output->wlr_output, event->state); + const struct wlr_output_state *state = event->state; + + // Store the requested changes so that the active configuration is + // consistent with the current state, and to avoid duplicate logic to apply + // the changes. + struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } + + int committed = state->committed; + if (committed & WLR_OUTPUT_STATE_MODE) { + if (state->mode != NULL) { + oc->width = state->mode->width; + oc->height = state->mode->height; + oc->refresh_rate = state->mode->refresh / 1000.f; + } else { + oc->width = state->custom_mode.width; + oc->height = state->custom_mode.height; + oc->refresh_rate = state->custom_mode.refresh / 1000.f; + } + committed &= ~WLR_OUTPUT_STATE_MODE; + } + if (committed & WLR_OUTPUT_STATE_SCALE) { + oc->scale = state->scale; + committed &= ~WLR_OUTPUT_STATE_SCALE; + } + if (committed & WLR_OUTPUT_STATE_TRANSFORM) { + oc->transform = state->transform; + committed &= ~WLR_OUTPUT_STATE_TRANSFORM; + } + + // We do not expect or support any other changes here + assert(committed == 0); + store_output_config(oc); + + force_modeset(); } static unsigned int last_headless_num = 0; @@ -1129,12 +623,24 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } - struct sway_output *output = output_create(wlr_output); - if (!output) { + // Create the scene output here so we're not accidentally creating one for + // the fallback output + struct wlr_scene_output *scene_output = + wlr_scene_output_create(root->root_scene, wlr_output); + if (!scene_output) { + sway_log(SWAY_ERROR, "Failed to create a scene output"); return; } + + struct sway_output *output = output_create(wlr_output); + if (!output) { + sway_log(SWAY_ERROR, "Failed to create a sway output"); + wlr_scene_output_destroy(scene_output); + return; + } + output->server = server; - wlr_damage_ring_init(&output->damage_ring); + output->scene_output = scene_output; wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); output->layout_destroy.notify = handle_layout_destroy; @@ -1144,35 +650,19 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->commit.notify = handle_commit; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&wlr_output->events.damage, &output->damage); - output->damage.notify = handle_damage; wl_signal_add(&wlr_output->events.frame, &output->frame); output->frame.notify = handle_frame; - wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); - output->needs_frame.notify = handle_needs_frame; wl_signal_add(&wlr_output->events.request_state, &output->request_state); output->request_state.notify = handle_request_state; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); - struct output_config *oc = find_output_config(output); - apply_output_config(oc, output); - free_output_config(oc); + if (server->session_lock.lock) { + sway_session_lock_add_output(server->session_lock.lock, output); + } - transaction_commit_dirty(); - - int width, height; - wlr_output_transformed_resolution(output->wlr_output, &width, &height); - wlr_damage_ring_set_bounds(&output->damage_ring, width, height); - update_output_manager_config(server); -} - -void handle_output_layout_change(struct wl_listener *listener, - void *data) { - struct sway_server *server = - wl_container_of(listener, server, output_layout_change); - update_output_manager_config(server); + request_modeset(); } void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { @@ -1190,74 +680,91 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { wlr_output_schedule_frame(output->wlr_output); } -static void output_manager_apply(struct sway_server *server, - struct wlr_output_configuration_v1 *config, bool test_only) { - // TODO: perform atomic tests on the whole backend atomically - - struct wlr_output_configuration_head_v1 *config_head; - // First disable outputs we need to disable - bool ok = true; - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!output->enabled || config_head->state.enabled) { - continue; - } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = false; - - if (test_only) { - ok &= test_output_config(oc, output); - } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); - } +static struct output_config *output_config_for_config_head( + struct wlr_output_configuration_head_v1 *config_head) { + struct output_config *oc = new_output_config(config_head->state.output->name); + if (!oc) { + return NULL; } - // Then enable outputs that need to - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!config_head->state.enabled) { - continue; - } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = true; - if (config_head->state.mode != NULL) { - struct wlr_output_mode *mode = config_head->state.mode; - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } else { - oc->width = config_head->state.custom_mode.width; - oc->height = config_head->state.custom_mode.height; - oc->refresh_rate = - config_head->state.custom_mode.refresh / 1000.f; - } - oc->x = config_head->state.x; - oc->y = config_head->state.y; - oc->transform = config_head->state.transform; - oc->scale = config_head->state.scale; - oc->adaptive_sync = config_head->state.adaptive_sync_enabled; - - if (test_only) { - ok &= test_output_config(oc, output); - } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); - } + oc->enabled = config_head->state.enabled; + if (!oc->enabled) { + return oc; } - if (ok) { - wlr_output_configuration_v1_send_succeeded(config); + if (config_head->state.mode != NULL) { + struct wlr_output_mode *mode = config_head->state.mode; + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh / 1000.f; } else { - wlr_output_configuration_v1_send_failed(config); + oc->width = config_head->state.custom_mode.width; + oc->height = config_head->state.custom_mode.height; + oc->refresh_rate = + config_head->state.custom_mode.refresh / 1000.f; } - wlr_output_configuration_v1_destroy(config); + oc->x = config_head->state.x; + oc->y = config_head->state.y; + oc->transform = config_head->state.transform; + oc->scale = config_head->state.scale; + oc->adaptive_sync = config_head->state.adaptive_sync_enabled; + return oc; +} - if (!test_only) { - update_output_manager_config(server); +static void output_manager_apply(struct sway_server *server, + struct wlr_output_configuration_v1 *cfg, bool test_only) { + bool ok = false; + size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); + struct output_config **configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + sway_log(SWAY_ERROR, "Allocation failed"); + goto error; } + size_t start_new_configs = config->output_configs->length; + for (size_t idx = 0; idx < start_new_configs; idx++) { + configs[idx] = config->output_configs->items[idx]; + } + + size_t config_idx = start_new_configs; + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &cfg->heads, link) { + // Generate the configuration and store it as a temporary + // config. We keep a record of it so we can remove it later. + struct output_config *oc = output_config_for_config_head(config_head); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + goto error_config; + } + configs[config_idx++] = oc; + } + + // Try to commit without degrade to off enabled. Note that this will fail + // if any output configured for enablement fails to be enabled, even if it + // was not part of the config heads we were asked to configure. + ok = apply_output_configs(configs, configs_len, test_only, false); + +error_config: + for (size_t idx = start_new_configs; idx < configs_len; idx++) { + struct output_config *cfg = configs[idx]; + if (!test_only && ok) { + store_output_config(cfg); + } else { + free_output_config(cfg); + } + } + free(configs); + +error: + if (ok) { + wlr_output_configuration_v1_send_succeeded(cfg); + if (server->delayed_modeset != NULL) { + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + } + } else { + wlr_output_configuration_v1_send_failed(cfg); + } + wlr_output_configuration_v1_destroy(cfg); } void handle_output_manager_apply(struct wl_listener *listener, void *data) { @@ -1282,6 +789,11 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output *output = event->output->data; struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } + switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: oc->power = 0; @@ -1290,6 +802,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, oc->power = 1; break; } - oc = store_output_config(oc); - apply_output_config(oc, output); + store_output_config(oc); + request_modeset(); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index a43797bb..00000000 --- a/sway/desktop/render.c +++ /dev/null @@ -1,1848 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "config.h" -#include "sway/config.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.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" -#include "sway/tree/view.h" -#include "sway/tree/workspace.h" - -static void transform_output_damage(pixman_region32_t *damage, struct wlr_output *output) { - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_region_transform(damage, damage, transform, ow, oh); -} - -static void transform_output_box(struct wlr_box *box, struct wlr_output *output) { - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(box, box, transform, ow, oh); -} - -// TODO: Remove this ugly abomination with a complete border rework... -static void transform_corner_location(enum corner_location *corner_location, struct wlr_output *output) { - if (*corner_location == ALL) { - return; - } - switch (wlr_output_transform_invert(output->transform)) { - case WL_OUTPUT_TRANSFORM_NORMAL: - return; - case WL_OUTPUT_TRANSFORM_90: - *corner_location = (*corner_location + 1) % 4; - return; - case WL_OUTPUT_TRANSFORM_180: - *corner_location = (*corner_location + 2) % 4; - return; - case WL_OUTPUT_TRANSFORM_270: - *corner_location = (*corner_location + 3) % 4; - return; - case WL_OUTPUT_TRANSFORM_FLIPPED: - *corner_location = (*corner_location + (1 - 2 * (*corner_location % 2))) % 4; - return; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - *corner_location = (*corner_location + (4 - 2 * (*corner_location % 2))) % 4; - return; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - *corner_location = (*corner_location + (3 - 2 * (*corner_location % 2))) % 4; - return; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - *corner_location = (*corner_location + (2 - 2 * (*corner_location % 2))) % 4; - return; - default: - return; - } -} - -struct decoration_data get_undecorated_decoration_data() { - return (struct decoration_data) { - .alpha = 1.0f, - .dim = 0.0f, - .dim_color = config->dim_inactive_colors.unfocused, - .corner_radius = 0, - .saturation = 1.0f, - .has_titlebar = false, - .blur = false, - .discard_transparent = false, - .shadow = false, - }; -} - -/** - * Apply scale to a width or height. - * - * One does not simply multiply the width by the scale. We allow fractional - * scaling, which means the resulting scaled width might be a decimal. - * So we round it. - * - * But even this can produce undesirable results depending on the X or Y offset - * of the box. For example, with a scale of 1.5, a box with width=1 should not - * scale to 2px if its X coordinate is 1, because the X coordinate would have - * scaled to 2px. - */ -static int scale_length(int length, int offset, float scale) { - return roundf((offset + length) * scale) - roundf(offset * scale); -} - -static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { - switch (output->scale_filter) { - case SCALE_FILTER_LINEAR: - return WLR_SCALE_FILTER_BILINEAR; - case SCALE_FILTER_NEAREST: - return WLR_SCALE_FILTER_NEAREST; - default: - abort(); // unreachable - } -} - -static void render_texture(struct fx_render_context *ctx, struct wlr_texture *texture, - const struct wlr_fbox *_src_box, const struct wlr_box *dst_box, - const struct wlr_box *_clip_box, enum wl_output_transform transform, - struct decoration_data deco_data) { - struct sway_output *output = ctx->output; - - struct wlr_box proj_box = *dst_box; - - struct wlr_fbox src_box = {0}; - if (_src_box) { - src_box = *_src_box; - } - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, proj_box.x, proj_box.y, - proj_box.width, proj_box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - - struct wlr_box clip_box = {0}; - if (_clip_box) { - pixman_region32_intersect_rect(&damage, &damage, - _clip_box->x, _clip_box->y, _clip_box->width, _clip_box->height); - - clip_box = *_clip_box; - } - - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - transform_output_box(&proj_box, output->wlr_output); - transform_output_box(&clip_box, output->wlr_output); - transform_output_damage(&damage, output->wlr_output); - transform = wlr_output_transform_compose(transform, output->wlr_output->transform); - - fx_render_pass_add_texture(ctx->pass, &(struct fx_render_texture_options) { - .base = { - .texture = texture, - .src_box = src_box, - .dst_box = proj_box, - .transform = transform, - .alpha = &deco_data.alpha, - .clip = &damage, - .filter_mode = get_scale_filter(output), - }, - .clip_box = &clip_box, - .corner_radius = deco_data.corner_radius, - .has_titlebar = deco_data.has_titlebar, - .dim = deco_data.dim, - .dim_color = { - .r = deco_data.dim_color[0], - .g = deco_data.dim_color[1], - .b = deco_data.dim_color[2], - .a = deco_data.dim_color[3], - } - }); - -damage_finish: - pixman_region32_fini(&damage); -} - -void render_blur(struct fx_render_context *ctx, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - bool optimized_blur, pixman_region32_t *opaque_region, - struct decoration_data deco_data) { - struct sway_output *output = ctx->output; - - struct wlr_box proj_box = *dst_box; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, proj_box.x, proj_box.y, - proj_box.width, proj_box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - - if (!pixman_region32_not_empty(&damage)) { - goto damage_finish; - } - - transform_output_box(&proj_box, output->wlr_output); - transform_output_damage(&damage, output->wlr_output); - - struct fx_render_blur_pass_options blur_options = { - .tex_options = { - .base = { - .texture = texture, - .src_box = *src_box, - .dst_box = proj_box, - .transform = WL_OUTPUT_TRANSFORM_NORMAL, - .clip = &damage, - .filter_mode = WLR_SCALE_FILTER_BILINEAR, - }, - .clip_box = &proj_box, - .corner_radius = deco_data.corner_radius, - .discard_transparent = false, - }, - .opaque_region = opaque_region, - .use_optimized_blur = optimized_blur, - .blur_data = &config->blur_params, - .ignore_transparent = deco_data.discard_transparent, - }; - // Render the actual blur behind the surface - fx_render_pass_add_blur(ctx->pass, &blur_options); - -damage_finish: - pixman_region32_fini(&damage); -} - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_box_shadow(struct fx_render_context *ctx, const struct wlr_box *_box, - const float color[static 4], float blur_sigma, float corner_radius, - float offset_x, float offset_y) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - - // Extend the size of the box while also considering the shadow offset - struct wlr_box shadow_box; - memcpy(&shadow_box, _box, sizeof(struct wlr_box)); - shadow_box.x -= blur_sigma - offset_x; - shadow_box.y -= blur_sigma - offset_y; - shadow_box.width += blur_sigma * 2; - shadow_box.height += blur_sigma * 2; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, shadow_box.x, shadow_box.y, - shadow_box.width, shadow_box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - if (!pixman_region32_not_empty(&damage)) { - goto damage_finish; - } - - transform_output_damage(&damage, wlr_output); - transform_output_box(&box, wlr_output); - transform_output_box(&shadow_box, wlr_output); - - struct shadow_data shadow_data = { - .enabled = true, - .offset_x = offset_x, - .offset_y = offset_y, - .color = { - .r = color[0], - .g = color[1], - .b = color[2], - .a = color[3], - }, - .blur_sigma = blur_sigma, - }; - struct fx_render_box_shadow_options shadow_options = { - .shadow_box = shadow_box, - .clip_box = box, - .clip = &damage, - .shadow_data = &shadow_data, - .corner_radius = corner_radius, - }; - fx_render_pass_add_box_shadow(ctx->pass, &shadow_options); - -damage_finish: - pixman_region32_fini(&damage); -} - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_rounded_border_corner(struct fx_render_context *ctx, const struct wlr_box *_box, - const float color[static 4], int corner_radius, int border_thickness, - enum corner_location location) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - - struct wlr_box box = *_box; - const int size = MAX(box.width, box.height); - box.width = size; - box.height = size; - box.x -= ctx->output->lx * wlr_output->scale; - box.y -= ctx->output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, box.x, box.y, box.width, box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - if (!pixman_region32_not_empty(&damage)) { - goto damage_finish; - } - - transform_output_damage(&damage, wlr_output); - transform_output_box(&box, wlr_output); - transform_corner_location(&location, wlr_output); - - struct fx_render_rounded_border_corner_options border_corner_options = { - .base = { - .box = box, - .color = { - .r = color[0], - .g = color[1], - .b = color[2], - .a = color[3], - }, - .clip = &damage, // Render with the original extended clip region - }, - .corner_radius = corner_radius, - .border_thickness = border_thickness, - .corner_location = location - }; - fx_render_pass_add_rounded_border_corner(ctx->pass, &border_corner_options); - -damage_finish: - pixman_region32_fini(&damage); -} - -static void render_surface_iterator(struct sway_output *output, - struct sway_view *_view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - return; - } - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - - struct wlr_box dst_box = *_box; - struct wlr_box clip_box = *_box; - if (data->clip_box != NULL) { - clip_box.width = fmin(dst_box.width, data->clip_box->width); - clip_box.height = fmin(dst_box.height, data->clip_box->height); - clip_box.x = fmax(dst_box.x, data->clip_box->x); - clip_box.y = fmax(dst_box.y, data->clip_box->y); - } - scale_box(&dst_box, wlr_output->scale); - scale_box(&clip_box, wlr_output->scale); - - struct decoration_data deco_data = data->deco_data; - deco_data.corner_radius *= wlr_output->scale; - - // render blur - struct sway_view *view = data->view; - bool is_subsurface = view ? view->surface != surface : false; - if (deco_data.blur && config_should_parameters_blur() && !is_subsurface) { - pixman_region32_t opaque_region; - pixman_region32_init(&opaque_region); - - bool has_alpha = false; - if (deco_data.alpha < 1.0 || deco_data.dim_color[3] < 1.0) { - has_alpha = true; - pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0); - } else { - has_alpha = !surface->opaque; - pixman_region32_copy(&opaque_region, &surface->opaque_region); - } - - if (has_alpha) { - bool should_optimize_blur = view ? - !container_is_floating_or_child(view->container) || config->blur_xray - : false; - render_blur(data->ctx, texture, &src_box, &clip_box, - should_optimize_blur, &opaque_region, deco_data); - } - - pixman_region32_fini(&opaque_region); - } - - deco_data.discard_transparent = false; - - render_texture(data->ctx, texture, - &src_box, &dst_box, &clip_box, surface->current.transform, deco_data); - - wlr_presentation_surface_textured_on_output(server.presentation, surface, - wlr_output); -} - -// view will be NULL every time -static void render_layer_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - // render the layer's surface - render_surface_iterator(output, view, surface, _box, _data); - - struct render_data *data = _data; - struct decoration_data deco_data = data->deco_data; - - // Ignore effects if this is a subsurface - if (!wlr_layer_surface_v1_try_from_wlr_surface(surface)) { - deco_data = get_undecorated_decoration_data(); - } - - // render shadow - if (deco_data.shadow && config_should_parameters_shadow()) { - float output_scale = output->wlr_output->scale; - struct wlr_box box = *_box; - scale_box(&box, output_scale); - render_box_shadow(data->ctx, &box, config->shadow_color, config->shadow_blur_sigma * output_scale, - deco_data.corner_radius * output_scale, config->shadow_offset_x * output_scale, config->shadow_offset_y * output_scale); - } -} - -static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces, - render_layer_iterator, &data); -} - - static void render_layer_popups(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - output_layer_for_each_popup_surface(ctx->output, layer_surfaces, - render_layer_iterator, &data); -} - -#if HAVE_XWAYLAND -static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unmanaged) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - output_unmanaged_for_each_surface(ctx->output, unmanaged, - render_surface_iterator, &data); -} -#endif - -static void render_input_popups(struct fx_render_context *ctx, struct wl_list *input_popups) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - output_input_popups_for_each_surface(ctx->output, input_popups, - render_surface_iterator, &data); -} - -static void render_drag_icons(struct fx_render_context *ctx, struct wl_list *drag_icons) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - output_drag_icons_for_each_surface(ctx->output, drag_icons, - render_surface_iterator, &data); -} - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - - struct wlr_box box = *_box; - box.x -= ctx->output->lx * wlr_output->scale; - box.y -= ctx->output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - transform_output_damage(&damage, wlr_output); - transform_output_box(&box, wlr_output); - - fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ - .base = { - .box = box, - .color = { - .r = color[0], - .g = color[1], - .b = color[2], - .a = color[3], - }, - .clip = &damage, - }, - }); - -damage_finish: - pixman_region32_fini(&damage); -} - -void render_rounded_rect(struct fx_render_context *ctx, const struct wlr_box *_box, - float color[static 4], int corner_radius, enum corner_location corner_location) { - if (!corner_radius) { - render_rect(ctx, _box, color); - return; - } - - struct wlr_output *wlr_output = ctx->output->wlr_output; - - struct wlr_box box = *_box; - box.x -= ctx->output->lx * wlr_output->scale; - box.y -= ctx->output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init_rect(&damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, ctx->output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - transform_output_damage(&damage, wlr_output); - transform_output_box(&box, wlr_output); - transform_corner_location(&corner_location, wlr_output); - - fx_render_pass_add_rounded_rect(ctx->pass, &(struct fx_render_rounded_rect_options){ - .base = { - .box = box, - .color = { - .r = color[0], - .g = color[1], - .b = color[2], - .a = color[3], - }, - .clip = &damage, - }, - .corner_radius = corner_radius, - .corner_location = corner_location - }); - -damage_finish: - pixman_region32_fini(&damage); -} - -void premultiply_alpha(float color[4], float opacity) { - color[3] *= opacity; - color[0] *= color[3]; - color[1] *= color[3]; - color[2] *= color[3]; -} - -static void render_view_toplevels(struct fx_render_context *ctx, - struct sway_view *view, struct decoration_data deco_data) { - struct render_data data = { - .deco_data = deco_data, - .view = view, - .ctx = ctx, - }; - // Clip the window to its view size, ignoring CSD - struct wlr_box clip_box; - struct sway_container_state state = view->container->current; - clip_box.x = floor(state.x) - ctx->output->lx; - clip_box.y = floor(state.y) - ctx->output->ly; - clip_box.width = state.width; - clip_box.height = state.height; - - bool smart = config->hide_edge_borders_smart == ESMART_ON || - (config->hide_edge_borders_smart == ESMART_NO_GAPS && - !gaps_to_edge(view)); - - if (state.fullscreen_mode == FULLSCREEN_NONE - && (state.border == B_PIXEL || state.border == B_NORMAL) - && !smart) { - clip_box.x += state.border_thickness; - clip_box.width -= state.border_thickness * 2; - - if (deco_data.has_titlebar) { - // Shift the box downward to compensate for the titlebar - int titlebar_thickness = container_titlebar_height(); - clip_box.y += titlebar_thickness; - clip_box.height -= state.border_thickness + titlebar_thickness; - } else { - // Regular border - clip_box.y += state.border_thickness; - clip_box.height -= state.border_thickness * 2; - } - } - data.clip_box = &clip_box; - - // Render all toplevels without descending into popups - double ox = view->container->surface_x - - ctx->output->lx - view->geometry.x; - double oy = view->container->surface_y - - ctx->output->ly - view->geometry.y; - output_surface_for_each_surface(ctx->output, view->surface, ox, oy, - render_surface_iterator, &data); -} - -static void render_view_popups(struct fx_render_context *ctx, struct sway_view *view, - struct decoration_data deco_data) { - struct render_data data = { - .deco_data = deco_data, - .view = view, - .ctx = ctx, - }; - output_view_for_each_popup_surface(ctx->output, view, - render_surface_iterator, &data); -} - -static void render_saved_view(struct fx_render_context *ctx, struct sway_view *view, struct decoration_data deco_data) { - struct sway_output *output = ctx->output; - struct wlr_output *wlr_output = output->wlr_output; - - if (wl_list_empty(&view->saved_buffers)) { - return; - } - - deco_data.corner_radius *= wlr_output->scale; - - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - if (!saved_buf->buffer->texture) { - continue; - } - - struct wlr_box proj_box = { - .x = saved_buf->x - view->saved_geometry.x - output->lx, - .y = saved_buf->y - view->saved_geometry.y - output->ly, - .width = saved_buf->width, - .height = saved_buf->height, - }; - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); - if (!intersects) { - continue; - } - - struct wlr_box dst_box = proj_box; - struct wlr_box clip_box = proj_box; - - // Clip to actual geometry, clipping the CSD - struct sway_container_state state = view->container->current; - clip_box.x = state.x - output->lx; - clip_box.y = state.y - output->ly; - clip_box.width = state.width; - clip_box.height = state.height; - if (state.border == B_PIXEL || state.border == B_NORMAL) { - clip_box.x += state.border_thickness; - clip_box.width -= state.border_thickness * 2; - - if (deco_data.has_titlebar) { - // Shift the box downward to compensate for the titlebar - int titlebar_thickness = container_titlebar_height(); - clip_box.y += titlebar_thickness; - clip_box.height -= state.border_thickness + titlebar_thickness; - } else { - // Regular border - clip_box.y += state.border_thickness; - clip_box.height -= state.border_thickness * 2; - } - } - - scale_box(&dst_box, wlr_output->scale); - scale_box(&clip_box, wlr_output->scale); - - // render blur - if (deco_data.blur && config_should_parameters_blur()) { - struct fx_texture_attribs attribs; - fx_texture_get_attribs(saved_buf->buffer->texture, &attribs); - if (deco_data.alpha < 1.0 || attribs.has_alpha) { - pixman_region32_t opaque_region; - pixman_region32_init(&opaque_region); - pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0); - - bool should_optimize_blur = !container_is_floating_or_child(view->container) || config->blur_xray; - render_blur(ctx, saved_buf->buffer->texture, - &saved_buf->source_box, &clip_box, should_optimize_blur, - &opaque_region, deco_data); - - pixman_region32_fini(&opaque_region); - } - } - - deco_data.discard_transparent = false; - - render_texture(ctx, saved_buf->buffer->texture, - &saved_buf->source_box, &dst_box, &clip_box, saved_buf->transform, deco_data); - - // FIXME: we should set the surface that this saved buffer originates from - // as sampled here. - // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 - } -} - -/** - * Render a view's surface, shadow, and left/bottom/right borders. - */ -static void render_view(struct fx_render_context *ctx, struct sway_container *con, - struct border_colors *colors, struct decoration_data deco_data) { - struct sway_view *view = con->view; - - if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(ctx, view, deco_data); - } else if (view->surface) { - render_view_toplevels(ctx, view, deco_data); - } - - struct sway_container_state *state = &con->current; - - if (state->border == B_CSD && !config->shadows_on_csd_enabled) { - return; - } - - struct wlr_box box; - int corner_radius = deco_data.corner_radius; - float output_scale = ctx->output->wlr_output->scale; - - // render shadow - if (con->shadow_enabled && config_should_parameters_shadow()) { - box.x = floor(state->x) - ctx->output->lx; - box.y = floor(state->y) - ctx->output->ly; - box.width = state->width; - box.height = state->height; - scale_box(&box, output_scale); - int shadow_corner_radius = corner_radius == 0 ? 0 : corner_radius + state->border_thickness; - float* shadow_color = view_is_urgent(view) || state->focused ? - config->shadow_color : config->shadow_inactive_color; - - render_box_shadow(ctx, &box, shadow_color, config->shadow_blur_sigma * output_scale, - shadow_corner_radius * output_scale, config->shadow_offset_x * output_scale, - config->shadow_offset_y * output_scale); - } - - if (state->border == B_NONE || state->border == B_CSD) { - return; - } - - float color[4]; - - if (state->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height - corner_radius; - if (corner_radius && !deco_data.has_titlebar) { - box.y += corner_radius; - box.height -= corner_radius; - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } - - list_t *siblings = container_get_current_siblings(con); - enum sway_container_layout layout = - container_current_parent_layout(con); - - if (state->border_right) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->content_x + state->content_width); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height - corner_radius; - if (corner_radius && !deco_data.has_titlebar) { - box.y += corner_radius; - box.height -= corner_radius; - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } - - if (state->border_bottom) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y + state->content_height); - box.width = state->width; - box.height = state->border_thickness; - // adjust sizing for rounded border corners - if (deco_data.corner_radius) { - box.x += corner_radius + state->border_thickness; - box.width -= 2 * (corner_radius + state->border_thickness); - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - if (corner_radius && state->border_thickness > 0) { - int size = 2 * (corner_radius + state->border_thickness); - int scaled_corner_radius = corner_radius * output_scale; - int scaled_border_thickness = state->border_thickness * output_scale; - if (state->border_left) { - box.x = floor(state->x); - box.y = floor(state->y + state->height - size); - box.width = size; - box.height = size; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, - scaled_border_thickness, BOTTOM_LEFT); - } - if (state->border_right) { - box.x = floor(state->x + state->width - size); - box.y = floor(state->y + state->height - size); - box.width = size; - box.height = size; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, - scaled_border_thickness, BOTTOM_RIGHT); - } - } - } -} - -/** - * Render a titlebar. - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. - * - * The height is: 1px border, 3px padding, font height, 3px padding, 1px border - * The left side is: 1px border, 2px padding, title - */ -static void render_titlebar(struct fx_render_context *ctx, struct sway_container *con, - int x, int y, int width, struct border_colors *colors, int corner_radius, - enum corner_location corner_location, struct wlr_texture *title_texture, - struct wlr_texture *marks_texture) { - struct wlr_box box; - float color[4]; - struct sway_output *output = ctx->output; - float output_scale = output->wlr_output->scale; - double output_x = output->lx; - double output_y = output->ly; - int titlebar_border_thickness = config->titlebar_border_thickness; - int titlebar_h_padding = config->titlebar_h_padding; - int titlebar_v_padding = config->titlebar_v_padding; - enum alignment title_align = config->title_align; - // value by which all heights should be adjusted to counteract removed bottom border - int bottom_border_compensation = config->titlebar_separator ? 0 : titlebar_border_thickness; - corner_radius *= output_scale; - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = y; - box.width = width; - box.height = titlebar_border_thickness; - if (corner_radius) { - if (corner_location != TOP_RIGHT) { - box.x += corner_radius; - } - if (corner_location == ALL) { - box.width -= corner_radius * 2; - } else { - box.width -= corner_radius; - } - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // Single pixel bar below title - if (!bottom_border_compensation) { - box.x = x; - box.y = y + container_titlebar_height() - titlebar_border_thickness; - box.width = width; - box.height = titlebar_border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - } - - // Single pixel left edge - box.x = x; - box.y = y; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness + bottom_border_compensation; - if (corner_radius && corner_location != TOP_RIGHT) { - box.height -= corner_radius; - box.y += corner_radius; - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // Single pixel right edge - box.x = x + width - titlebar_border_thickness; - box.y = y; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness + bottom_border_compensation; - if (corner_radius && corner_location != TOP_LEFT) { - box.height -= corner_radius; - box.y += corner_radius; - } - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - // if corner_radius: single pixel corners - if (corner_radius) { - // left corner - if (corner_location != TOP_RIGHT) { - box.x = x; - box.y = y; - box.width = corner_radius * 2; - box.height = corner_radius * 2; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, corner_radius, - titlebar_border_thickness * output_scale, TOP_LEFT); - } - - // right corner - if (corner_location != TOP_LEFT) { - box.x = x + width - corner_radius * 2; - box.y = y; - box.width = corner_radius * 2; - box.height = corner_radius * 2; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, corner_radius, - titlebar_border_thickness * output_scale, TOP_RIGHT); - } - } - - int inner_x = x - output_x + titlebar_h_padding; - int bg_y = y + titlebar_border_thickness; - size_t inner_width = width - titlebar_h_padding * 2; - - // output-buffer local - int ob_inner_x = roundf(inner_x * output_scale); - int ob_inner_width = scale_length(inner_width, inner_x, output_scale); - int ob_bg_height = scale_length( - (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height, bg_y, output_scale); - - // title marks textures should have no eyecandy - struct decoration_data deco_data = get_undecorated_decoration_data(); - deco_data.alpha = con->alpha; - - // Marks - int ob_marks_x = 0; // output-buffer-local - int ob_marks_width = 0; // output-buffer-local - if (config->show_marks && marks_texture) { - struct wlr_box texture_box = { - .width = marks_texture->width, - .height = marks_texture->height, - }; - ob_marks_width = texture_box.width; - - // The marks texture might be shorter than the config->font_height, in - // which case we need to pad it as evenly as possible above and below. - int ob_padding_total = ob_bg_height - texture_box.height; - int ob_padding_above = floor(ob_padding_total / 2.0); - int ob_padding_below = ceil(ob_padding_total / 2.0); - - // Render texture. If the title is on the right, the marks will be on - // the left. Otherwise, they will be on the right. - if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { - texture_box.x = ob_inner_x; - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_marks_x = texture_box.x; - - texture_box.y = round((bg_y - output_y) * output_scale) + - ob_padding_above; - - struct wlr_box clip_box = texture_box; - if (ob_inner_width < clip_box.width) { - clip_box.width = ob_inner_width; - } - render_texture(ctx, marks_texture, - NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = clip_box.x + round(output_x * output_scale); - box.y = roundf((y + titlebar_border_thickness) * output_scale); - box.width = clip_box.width; - box.height = ob_padding_above; - render_rect(ctx, &box, color); - - // Padding below - box.y += ob_padding_above + clip_box.height; - box.height = ob_padding_below + bottom_border_compensation; - render_rect(ctx, &box, color); - } - - // Title text - int ob_title_x = 0; // output-buffer-local - int ob_title_width = 0; // output-buffer-local - if (title_texture) { - struct wlr_box texture_box = { - .width = title_texture->width, - .height = title_texture->height, - }; - - // The effective output may be NULL when con is not on any output. - // This can happen because we render all children of containers, - // even those that are out of the bounds of any output. - struct sway_output *effective = container_get_effective_output(con); - float title_scale = effective ? effective->wlr_output->scale : output_scale; - texture_box.width = texture_box.width * output_scale / title_scale; - texture_box.height = texture_box.height * output_scale / title_scale; - ob_title_width = texture_box.width; - - // The title texture might be shorter than the config->font_height, - // in which case we need to pad it above and below. - int ob_padding_above = roundf((titlebar_v_padding - - titlebar_border_thickness) * output_scale); - int ob_padding_below = ob_bg_height - ob_padding_above - - texture_box.height; - - // Render texture - if (texture_box.width > ob_inner_width - ob_marks_width) { - texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) - ? ob_marks_x + ob_marks_width : ob_inner_x; - } else if (title_align == ALIGN_LEFT) { - texture_box.x = ob_inner_x; - } else if (title_align == ALIGN_CENTER) { - // If there are marks visible, center between the edge and marks. - // Otherwise, center in the inner area. - if (ob_marks_width) { - texture_box.x = (ob_inner_x + ob_marks_x) / 2 - - texture_box.width / 2; - } else { - texture_box.x = ob_inner_x + ob_inner_width / 2 - - texture_box.width / 2; - } - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_title_x = texture_box.x; - - texture_box.y = - round((bg_y - output_y) * output_scale) + ob_padding_above; - - struct wlr_box clip_box = texture_box; - if (ob_inner_width - ob_marks_width < clip_box.width) { - clip_box.width = ob_inner_width - ob_marks_width; - } - - render_texture(ctx, title_texture, - NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = clip_box.x + round(output_x * output_scale); - box.y = roundf((y + titlebar_border_thickness) * output_scale); - box.width = clip_box.width; - box.height = ob_padding_above; - render_rect(ctx, &box, color); - - // Padding below - box.y += ob_padding_above + clip_box.height; - box.height = ob_padding_below + bottom_border_compensation; - render_rect(ctx, &box, color); - } - - // Determine the left + right extends of the textures (output-buffer local) - int ob_left_x, ob_left_width, ob_right_x, ob_right_width; - if (ob_title_width == 0 && ob_marks_width == 0) { - ob_left_x = ob_inner_x; - ob_left_width = 0; - ob_right_x = ob_inner_x; - ob_right_width = 0; - } else if (ob_title_x < ob_marks_x) { - ob_left_x = ob_title_x; - ob_left_width = ob_title_width; - ob_right_x = ob_marks_x; - ob_right_width = ob_marks_width; - } else { - ob_left_x = ob_marks_x; - ob_left_width = ob_marks_width; - ob_right_x = ob_title_x; - ob_right_width = ob_title_width; - } - if (ob_left_x < ob_inner_x) { - ob_left_x = ob_inner_x; - } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { - ob_right_x = ob_left_x; - ob_right_width = ob_left_width; - } - - // Filler between title and marks - box.width = ob_right_x - ob_left_x - ob_left_width; - if (box.width > 0) { - box.x = ob_left_x + ob_left_width + round(output_x * output_scale); - box.y = roundf(bg_y * output_scale); - box.height = ob_bg_height + bottom_border_compensation; - render_rect(ctx, &box, color); - } - - // Padding on left side - box.x = x + titlebar_border_thickness; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height + bottom_border_compensation; - scale_box(&box, output_scale); - int left_x = ob_left_x + round(output_x * output_scale); - if (box.x + box.width < left_x) { - box.width += left_x - box.x - box.width; - } - if (corner_radius && (corner_location == TOP_LEFT || corner_location == ALL)) { - render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT); - } else { - render_rect(ctx, &box, color); - } - - // Padding on right side - box.x = x + width - titlebar_h_padding; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height + bottom_border_compensation; - scale_box(&box, output_scale); - int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); - if (right_rx < box.x) { - box.width += box.x - right_rx; - box.x = right_rx; - } - if (corner_radius && (corner_location == TOP_RIGHT || corner_location == ALL)) { - render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT); - } else { - render_rect(ctx, &box, color); - } -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct fx_render_context *ctx, struct sway_container *con, - struct border_colors *colors, int corner_radius) { - struct sway_container_state *state = &con->current; - if (!state->border_top) { - return; - } - struct wlr_box box; - float color[4]; - float output_scale = ctx->output->wlr_output->scale; - - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x) + - (corner_radius != 0) * (corner_radius + state->border_thickness); - box.y = floor(state->y); - box.width = state->width - - ((corner_radius != 0) * 2 * (corner_radius + state->border_thickness)); - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(ctx, &box, color); - - if (corner_radius && state->border_thickness > 0) { - int size = 2 * (corner_radius + state->border_thickness); - int scaled_corner_radius = corner_radius * output_scale; - int scaled_border_thickness = state->border_thickness * output_scale; - if (state->border_left) { - box.x = floor(state->x); - box.y = floor(state->y); - box.width = size; - box.height = size; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, - scaled_border_thickness, TOP_LEFT); - } - if (state->border_right) { - box.x = floor(state->x + state->width - size); - box.y = floor(state->y); - box.width = size; - box.height = size; - scale_box(&box, output_scale); - render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, - scaled_border_thickness, TOP_RIGHT); - } - } -} - -struct parent_data { - enum sway_container_layout layout; - struct wlr_box box; - list_t *children; - bool focused; - struct sway_container *active_child; -}; - -static void render_container(struct fx_render_context *ctx, - struct sway_container *con, bool parent_focused); - -// TODO: no rounding top corners when rendering with titlebar -/** - * Render a container's children using a L_HORIZ or L_VERT layout. - * - * Wrap child views in borders and leave child containers borderless because - * they'll apply their own borders to their children. - */ -static void render_containers_linear(struct fx_render_context *ctx, struct parent_data *parent) { - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - - if (container_is_scratchpad_hidden(child)) { - continue; - } - - if (child->view) { - struct sway_view *view = child->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - struct sway_container_state *state = &child->current; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (state->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - bool has_titlebar = state->border == B_NORMAL; - - struct decoration_data deco_data = { - .alpha = child->alpha, - .dim_color = view_is_urgent(view) - ? config->dim_inactive_colors.urgent - : config->dim_inactive_colors.unfocused, - .dim = child->current.focused || parent->focused ? 0.0f : child->dim, - // no corner radius if no gaps (allows smart_gaps to work as expected) - .corner_radius = config->smart_corner_radius && - ctx->output->current.active_workspace->current_gaps.top == 0 - ? 0 : child->corner_radius, - .saturation = child->saturation, - .has_titlebar = has_titlebar, - .blur = child->blur_enabled, - .discard_transparent = false, - .shadow = child->shadow_enabled, - }; - render_view(ctx, child, colors, deco_data); - if (has_titlebar) { - render_titlebar(ctx, child, floor(state->x), floor(state->y), - state->width, colors, deco_data.corner_radius, - ALL, title_texture, marks_texture); - } else if (state->border == B_PIXEL) { - render_top_border(ctx, child, colors, deco_data.corner_radius); - } - } else { - render_container(ctx, child, - parent->focused || child->current.focused); - } - } -} - -static bool container_is_focused(struct sway_container *con, void *data) { - return con->current.focused; -} - -static bool container_has_focused_child(struct sway_container *con) { - return container_find_child(con, container_is_focused, NULL); -} - -/** - * Render a container's children using the L_TABBED layout. - */ -static void render_containers_tabbed(struct fx_render_context *ctx, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - int tab_width = parent->box.width / parent->children->length; - - struct decoration_data deco_data = { - .alpha = current->alpha, - .dim_color = current->view && view_is_urgent(current->view) - ? config->dim_inactive_colors.urgent - : config->dim_inactive_colors.unfocused, - .dim = current->current.focused || parent->focused ? 0.0f : current->dim, - // no corner radius if no gaps (allows smart_gaps to work as expected) - .corner_radius = config->smart_corner_radius && - ctx->output->current.active_workspace->current_gaps.top == 0 - ? 0 : current->corner_radius, - .saturation = current->saturation, - .has_titlebar = true, - .blur = current->blur_enabled, - .discard_transparent = false, - .shadow = current->shadow_enabled, - }; - - // Render tabs - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int x = floor(cstate->x + tab_width * i); - - // Make last tab use the remaining width of the parent - if (i == parent->children->length - 1) { - tab_width = parent->box.width - tab_width * i; - } - - // only round outer corners - int corner_radius = deco_data.corner_radius; - enum corner_location corner_location = ALL; - if (parent->children->length > 1) { - if (i == 0) { - corner_location = TOP_LEFT; - } else if (i == parent->children->length - 1) { - corner_location = TOP_RIGHT; - } else { - corner_radius = 0; - } - } - - render_titlebar(ctx, child, x, parent->box.y, tab_width, colors, - corner_radius, corner_location, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(ctx, current, current_colors, deco_data); - } else { - render_container(ctx, current, - parent->focused || current->current.focused); - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_containers_stacked(struct fx_render_context *ctx, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - size_t titlebar_height = container_titlebar_height(); - - struct decoration_data deco_data = { - .alpha = current->alpha, - .dim_color = current->view && view_is_urgent(current->view) - ? config->dim_inactive_colors.urgent - : config->dim_inactive_colors.unfocused, - .dim = current->current.focused || parent->focused ? 0.0f : current->dim, - .saturation = current->saturation, - .corner_radius = config->smart_corner_radius && - ctx->output->current.active_workspace->current_gaps.top == 0 - ? 0 : current->corner_radius, - .has_titlebar = true, - .blur = current->blur_enabled, - .discard_transparent = false, - .shadow = current->shadow_enabled, - }; - - // Render titles - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int y = parent->box.y + titlebar_height * i; - int corner_radius = i != 0 ? 0 : deco_data.corner_radius; - render_titlebar(ctx, child, parent->box.x, y, parent->box.width, colors, - corner_radius, ALL, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(ctx, current, current_colors, deco_data); - } else { - render_container(ctx, current, - parent->focused || current->current.focused); - } -} - -static void render_containers(struct fx_render_context *ctx, struct parent_data *parent) { - if (config->hide_lone_tab && parent->children->length == 1) { - struct sway_container *child = parent->children->items[0]; - if (child->view) { - render_containers_linear(ctx, parent); - return; - } - } - - switch (parent->layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_containers_linear(ctx, parent); - break; - case L_STACKED: - render_containers_stacked(ctx, parent); - break; - case L_TABBED: - render_containers_tabbed(ctx, parent); - break; - } -} - -static void render_container(struct fx_render_context *ctx, - struct sway_container *con, bool focused) { - struct parent_data data = { - .layout = con->current.layout, - .box = { - .x = floor(con->current.x), - .y = floor(con->current.y), - .width = con->current.width, - .height = con->current.height, - }, - .children = con->current.children, - .focused = focused, - .active_child = con->current.focused_inactive_child, - }; - render_containers(ctx, &data); -} - -static void render_workspace(struct fx_render_context *ctx, - struct sway_workspace *ws, bool focused) { - struct parent_data data = { - .layout = ws->current.layout, - .box = { - .x = floor(ws->current.x), - .y = floor(ws->current.y), - .width = ws->current.width, - .height = ws->current.height, - }, - .children = ws->current.tiling, - .focused = focused, - .active_child = ws->current.focused_inactive_child, - }; - render_containers(ctx, &data); -} - -static void render_floating_container(struct fx_render_context *ctx, - struct sway_container *con) { - struct sway_container_state *state = &con->current; - if (con->view) { - struct sway_view *view = con->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = con->title_urgent; - marks_texture = con->marks_urgent; - } else if (state->focused) { - colors = &config->border_colors.focused; - title_texture = con->title_focused; - marks_texture = con->marks_focused; - } else { - colors = &config->border_colors.unfocused; - title_texture = con->title_unfocused; - marks_texture = con->marks_unfocused; - } - - bool has_titlebar = state->border == B_NORMAL; - struct decoration_data deco_data = { - .alpha = con->alpha, - .dim_color = view_is_urgent(view) - ? config->dim_inactive_colors.urgent - : config->dim_inactive_colors.unfocused, - .dim = con->current.focused ? 0.0f : con->dim, - .saturation = con->saturation, - .corner_radius = con->corner_radius, - .has_titlebar = has_titlebar, - .blur = con->blur_enabled, - .discard_transparent = false, - .shadow = con->shadow_enabled, - }; - render_view(ctx, con, colors, deco_data); - if (has_titlebar) { - render_titlebar(ctx, con, floor(con->current.x), floor(con->current.y), con->current.width, - colors, deco_data.corner_radius, ALL, title_texture, marks_texture); - } else if (state->border == B_PIXEL) { - render_top_border(ctx, con, colors, deco_data.corner_radius); - } - } else { - render_container(ctx, con, state->focused); - } -} - -static void render_floating(struct fx_render_context *ctx) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->current.workspaces->length; ++j) { - struct sway_workspace *ws = output->current.workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - for (int k = 0; k < ws->current.floating->length; ++k) { - struct sway_container *floater = ws->current.floating->items[k]; - if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { - continue; - } - render_floating_container(ctx, floater); - } - } - } -} - -static void render_seatops(struct fx_render_context *ctx) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seatop_render(seat, ctx); - } -} - -void output_render(struct fx_render_context *ctx) { - struct wlr_output *wlr_output = ctx->output->wlr_output; - struct sway_output *output = ctx->output; - pixman_region32_t *damage = ctx->output_damage; - - struct fx_effect_framebuffers *effect_fbos = ctx->pass->fx_effect_framebuffers; - - struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - return; - } - - if (debug.damage == DAMAGE_HIGHLIGHT) { - fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ - .base = { - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 1, .g = 1, .b = 0, .a = 1 }, - }, - }); - } - - pixman_region32_t transformed_damage; - pixman_region32_init(&transformed_damage); - pixman_region32_copy(&transformed_damage, damage); - transform_output_damage(&transformed_damage, wlr_output); - - if (server.session_lock.locked) { - struct wlr_render_color clear_color = { - .a = 1.0f - }; - if (server.session_lock.lock == NULL) { - // abandoned lock -> red BG - clear_color.r = 1.f; - } - - fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ - .base = { - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = clear_color, - .clip = &transformed_damage, - }, - }); - - if (server.session_lock.lock != NULL) { - struct render_data data = { - .deco_data = get_undecorated_decoration_data(), - .ctx = ctx, - }; - - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != wlr_output) { - continue; - } - if (!lock_surface->surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, render_surface_iterator, &data); - } - } - goto renderer_end; - } - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - - if (output_has_opaque_overlay_layer_surface(output)) { - goto render_overlay; - } - - if (fullscreen_con) { - fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ - .base = { - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, - .clip = &transformed_damage, - }, - }); - - if (fullscreen_con->view) { - struct decoration_data deco_data = get_undecorated_decoration_data(); - deco_data.saturation = fullscreen_con->saturation; - if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(ctx, fullscreen_con->view, deco_data); - } else if (fullscreen_con->view->surface) { - render_view_toplevels(ctx, fullscreen_con->view, deco_data); - } - } else { - render_container(ctx, fullscreen_con, fullscreen_con->current.focused); - } - - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - render_floating_container(ctx, floater); - } - } -#if HAVE_XWAYLAND - render_unmanaged(ctx, &root->xwayland_unmanaged); -#endif - } else { - int output_width, output_height; - wlr_output_transformed_resolution(wlr_output, &output_width, &output_height); - - pixman_region32_t blur_region; - pixman_region32_init(&blur_region); - bool workspace_has_blur = workspace_get_blur_info(workspace, &blur_region); - // Expand the damage to compensate for blur - if (effect_fbos && workspace_has_blur) { - // Skip the blur artifact prevention if damaging the whole viewport - if (effect_fbos->blur_buffer_dirty) { - // Needs to be extended before clearing - pixman_region32_union_rect(damage, damage, - 0, 0, output_width, output_height); - } else { - // copy the surrounding content where the blur would display artifacts - // and draw it above the artifacts - - // Expand the original damage to compensate for surrounding - // blurred views to avoid sharp edges between damage regions - wlr_region_expand(damage, damage, config_get_blur_size()); - - pixman_region32_t extended_damage; - pixman_region32_init(&extended_damage); - pixman_region32_intersect(&extended_damage, damage, &blur_region); - // Expand the region to compensate for blur artifacts - wlr_region_expand(&extended_damage, &extended_damage, config_get_blur_size()); - // Limit to the monitors viewport - pixman_region32_intersect_rect(&extended_damage, &extended_damage, - 0, 0, output_width, output_height); - - // capture the padding pixels around the blur where artifacts will be drawn - pixman_region32_subtract(&effect_fbos->blur_padding_region, - &extended_damage, damage); - // Combine into the surface damage (we need to redraw the padding area as well) - pixman_region32_union(damage, damage, &extended_damage); - pixman_region32_fini(&extended_damage); - - // Copy the new extended damage into the transformed damage - pixman_region32_copy(&transformed_damage, damage); - transform_output_damage(&transformed_damage, wlr_output); - - // Capture the padding pixels before blur for later use - fx_renderer_read_to_buffer(ctx->pass, &effect_fbos->blur_padding_region, - effect_fbos->blur_saved_pixels_buffer, - ctx->pass->buffer, true); - } - } - pixman_region32_fini(&blur_region); - - fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ - .base = { - .box = { .width = wlr_output->width, .height = wlr_output->height }, - .color = { .r = 0.25f, .g = 0.25f, .b = 0.25f, .a = 1 }, - .clip = &transformed_damage, - }, - }); - - render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - // Check if the background needs to be blurred. - // Render optimized/x-ray blur - if (effect_fbos && workspace_has_blur && effect_fbos->blur_buffer_dirty) { - const float opacity = 1.0f; - struct fx_render_blur_pass_options blur_options = { - .tex_options = { - .base = { - .transform = WL_OUTPUT_TRANSFORM_NORMAL, - .alpha = &opacity, - .blend_mode = WLR_RENDER_BLEND_MODE_NONE, - }, - .corner_radius = 0, - .discard_transparent = false, - }, - .blur_data = &config->blur_params, - }; - fx_render_pass_add_optimized_blur(ctx->pass, &blur_options); - } - - render_workspace(ctx, workspace, workspace->current.focused); - render_floating(ctx); -#if HAVE_XWAYLAND - render_unmanaged(ctx, &root->xwayland_unmanaged); -#endif - render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - - render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - } - - render_seatops(ctx); - - if (focus && focus->view) { - struct decoration_data deco_data = { - .alpha = focus->alpha, - .dim_color = view_is_urgent(focus->view) - ? config->dim_inactive_colors.urgent - : config->dim_inactive_colors.unfocused, - .dim = focus->current.focused ? 0.0f : focus->dim, - .corner_radius = 0, - .saturation = focus->saturation, - .has_titlebar = false, - .blur = false, - .discard_transparent = false, - .shadow = false, - }; - render_view_popups(ctx, focus->view, deco_data); - } - -render_overlay: - render_layer_toplevel(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(ctx, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_input_popups(ctx, &seat->im_relay.input_popups); - render_drag_icons(ctx, &root->drag_icons); - -renderer_end: - // Not needed if we damaged the whole viewport - if (effect_fbos && !effect_fbos->blur_buffer_dirty) { - // Render the saved pixels over the blur artifacts - fx_renderer_read_to_buffer(ctx->pass, &effect_fbos->blur_padding_region, - ctx->pass->buffer, effect_fbos->blur_saved_pixels_buffer, true); - } - - pixman_region32_fini(&transformed_damage); - wlr_output_add_software_cursors_to_render_pass(wlr_output, &ctx->pass->base, damage); -} diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 5932eaa2..00000000 --- a/sway/desktop/surface.c +++ /dev/null @@ -1,72 +0,0 @@ -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include -#include "sway/server.h" -#include "sway/surface.h" -#include "sway/output.h" - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_surface *surface = wl_container_of(listener, surface, destroy); - - surface->wlr_surface->data = NULL; - wl_list_remove(&surface->destroy.link); - - if (surface->frame_done_timer) { - wl_event_source_remove(surface->frame_done_timer); - } - - free(surface); -} - -static int surface_frame_done_timer_handler(void *data) { - struct sway_surface *surface = data; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(surface->wlr_surface, &now); - - return 0; -} - -void handle_compositor_new_surface(struct wl_listener *listener, void *data) { - struct wlr_surface *wlr_surface = data; - - struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); - surface->wlr_surface = wlr_surface; - wlr_surface->data = surface; - - surface->destroy.notify = handle_destroy; - wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); - - surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - surface_frame_done_timer_handler, surface); - if (!surface->frame_done_timer) { - wl_resource_post_no_memory(wlr_surface->resource); - } -} - -void surface_update_outputs(struct wlr_surface *surface) { - float scale = 1; - struct wlr_surface_output *surface_output; - wl_list_for_each(surface_output, &surface->current_outputs, link) { - if (surface_output->output->scale > scale) { - scale = surface_output->output->scale; - } - } - wlr_fractional_scale_v1_notify_scale(surface, scale); - wlr_surface_set_preferred_buffer_scale(surface, ceil(scale)); -} - -void surface_enter_output(struct wlr_surface *surface, - struct sway_output *output) { - wlr_surface_send_enter(surface, output->wlr_output); - surface_update_outputs(surface); -} - -void surface_leave_output(struct wlr_surface *surface, - struct sway_output *output) { - wlr_surface_send_leave(surface, output->wlr_output); - surface_update_outputs(surface); -} diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c new file mode 100644 index 00000000..d8d27645 --- /dev/null +++ b/sway/desktop/tearing.c @@ -0,0 +1,64 @@ +#include +#include +#include "sway/server.h" +#include "sway/tree/view.h" +#include "sway/output.h" +#include "log.h" + +struct sway_tearing_controller { + struct wlr_tearing_control_v1 *tearing_control; + struct wl_listener set_hint; + struct wl_listener destroy; + + struct wl_list link; // sway_server::tearing_controllers +}; + +static void handle_tearing_controller_set_hint(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, set_hint); + + struct sway_view *view = view_from_wlr_surface( + controller->tearing_control->surface); + if (view) { + view->tearing_hint = controller->tearing_control->current; + } +} + +static void handle_tearing_controller_destroy(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->set_hint.link); + wl_list_remove(&controller->destroy.link); + wl_list_remove(&controller->link); + free(controller); +} + +void handle_new_tearing_hint(struct wl_listener *listener, + void *data) { + struct sway_server *server = + wl_container_of(listener, server, tearing_control_new_object); + struct wlr_tearing_control_v1 *tearing_control = data; + + enum wp_tearing_control_v1_presentation_hint hint = + wlr_tearing_control_manager_v1_surface_hint_from_surface( + server->tearing_control_v1, tearing_control->surface); + sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", + hint, tearing_control->surface); + + struct sway_tearing_controller *controller = + calloc(1, sizeof(struct sway_tearing_controller)); + if (!controller) { + return; + } + + controller->tearing_control = tearing_control; + controller->set_hint.notify = handle_tearing_controller_set_hint; + wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); + controller->destroy.notify = handle_tearing_controller_destroy; + wl_signal_add(&tearing_control->events.destroy, &controller->destroy); + wl_list_init(&controller->link); + + wl_list_insert(&server->tearing_controllers, &controller->link); +} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 6947e138..23311568 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -1,16 +1,18 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include #include #include +#include "scenefx/types/fx/clipped_region.h" +#include "scenefx/types/fx/corner_location.h" #include "sway/config.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #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" @@ -214,39 +216,20 @@ static void transaction_add_node(struct sway_transaction *transaction, static void apply_output_state(struct sway_output *output, struct sway_output_state *state) { - output_damage_whole(output); list_free(output->current.workspaces); memcpy(&output->current, state, sizeof(struct sway_output_state)); - output_damage_whole(output); } static void apply_workspace_state(struct sway_workspace *ws, struct sway_workspace_state *state) { - output_damage_whole(ws->current.output); list_free(ws->current.floating); list_free(ws->current.tiling); memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); - output_damage_whole(ws->current.output); } static void apply_container_state(struct sway_container *container, struct sway_container_state *state) { struct sway_view *view = container->view; - // Damage the old location - desktop_damage_whole_container(container); - if (view && !wl_list_empty(&view->saved_buffers)) { - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - struct wlr_box box = { - .x = saved_buf->x - view->saved_geometry.x, - .y = saved_buf->y - view->saved_geometry.y, - .width = saved_buf->width, - .height = saved_buf->height, - }; - desktop_damage_box(&box); - } - } - // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -256,35 +239,549 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view && !wl_list_empty(&view->saved_buffers)) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { - view_remove_saved_buffer(view); + if (view) { + if (view->saved_surface_tree) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(view); + } + } + + // If the view hasn't responded to the configure, center it within + // the container. This is important for fullscreen views which + // refuse to resize to the size of the output. + if (view->surface) { + view_center_and_clip_surface(view); + } + } +} + +static void arrange_title_bar(struct sway_container *con, + int x, int y, int width, int height) { + container_update(con); + + bool has_title_bar = height > 0; + wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar); + if (!has_title_bar) { + return; + } + + wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); + + con->title_width = width; + container_arrange_title_bar(con); +} + +static void disable_container(struct sway_container *con) { + if (con->view) { + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + } else { + for (int i = 0; i < con->current.children->length; i++) { + struct sway_container *child = con->current.children->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + + disable_container(child); + } + } +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps); + +static void arrange_children(enum sway_container_layout layout, list_t *children, + struct sway_container *active, struct wlr_scene_tree *content, + int width, int height, int gaps) { + int title_bar_height = container_titlebar_height(); + + if (layout == L_TABBED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + double w = (double) width / children->length; + int title_offset = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + int next_title_offset = round(w * i + w); + + arrange_title_bar(child, title_offset, -title_bar_height, + next_title_offset - title_offset, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_enabled(&child->shadow->node, false); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_bar_height, + title_bar_height == 0, 0); + } else { + disable_container(child); + } + + title_offset = next_title_offset; + } + } else if (layout == L_STACKED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + int title_height = title_bar_height * children->length; + + int y = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + + arrange_title_bar(child, 0, y - title_height, width, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_enabled(&child->shadow->node, false); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_height, + title_bar_height == 0, 0); + } else { + disable_container(child); + } + + y += title_bar_height; + } + } else if (layout == L_VERT) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cheight = child->current.height; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_enabled(&child->shadow->node, + container_has_shadow(child) && child->view); + wlr_scene_node_set_position(&child->scene_tree->node, 0, off); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, width, cheight, true, gaps); + off += cheight + gaps; + } + } else if (layout == L_HORIZ) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cwidth = child->current.width; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_enabled(&child->shadow->node, + container_has_shadow(child) && child->view); + wlr_scene_node_set_position(&child->scene_tree->node, off, 0); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, cwidth, height, true, gaps); + off += cwidth + gaps; + } + } else { + sway_assert(false, "unreachable"); + } +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps) { + // this container might have previously been in the scratchpad, + // make sure it's enabled for viewing + wlr_scene_node_set_enabled(&con->scene_tree->node, true); + + if (con->output_handler) { + wlr_scene_buffer_set_dest_size(con->output_handler, width, height); + } + + bool has_corner_radius = container_has_corner_radius(con); + + if (container_has_shadow(con)) { + int corner_radius = has_corner_radius ? con->corner_radius + con->current.border_thickness : 0; + if (!con->view && title_bar) { + // Stacking/Tabbed containers don't have a border_thickness, so we + // use the config default + corner_radius = con->corner_radius + config->border_thickness; + } + + wlr_scene_shadow_set_size(con->shadow, + width + config->shadow_blur_sigma * 2, + height + config->shadow_blur_sigma * 2); + + int x = config->shadow_offset_x - config->shadow_blur_sigma; + int y = config->shadow_offset_y - config->shadow_blur_sigma; + wlr_scene_node_set_position(&con->shadow->node, x, y); + + wlr_scene_shadow_set_clipped_region(con->shadow, (struct clipped_region) { + .corner_radius = corner_radius, + .corners = CORNER_LOCATION_ALL, + .area = { + .x = config->shadow_blur_sigma - config->shadow_offset_x, + .y = config->shadow_blur_sigma - config->shadow_offset_y, + .width = width, + .height = height, + } + }); + + bool is_urgent = con->view && view_is_urgent(con->view); + float* shadow_color = is_urgent || con->current.focused ? + config->shadow_color : config->shadow_inactive_color; + wlr_scene_shadow_set_color(con->shadow, shadow_color); + wlr_scene_shadow_set_blur_sigma(con->shadow, config->shadow_blur_sigma); + wlr_scene_shadow_set_corner_radius(con->shadow, + !has_corner_radius ? 0 : corner_radius); + } + + if (con->view) { + int corner_radius = has_corner_radius ? con->corner_radius : 0; + int border_top = container_titlebar_height(); + int border_width = con->current.border_thickness; + int vert_border_offset = corner_radius; + + if (title_bar && con->current.border != B_NORMAL) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + wlr_scene_node_set_enabled(&con->border.top->node, true); + } else { + wlr_scene_node_set_enabled(&con->border.top->node, false); + } + + if (con->current.border == B_NORMAL) { + vert_border_offset = 0; + if (title_bar) { + arrange_title_bar(con, 0, 0, width, border_top); + } else { + border_top = 0; + // should be handled by the parent container + } + } else if (con->current.border == B_PIXEL) { + container_update(con); + border_top = title_bar && con->current.border_top ? border_width : 0; + if (!title_bar && !con->current.border_top) { + vert_border_offset = 0; + } + } else if (con->current.border == B_NONE) { + container_update(con); + border_top = 0; + border_width = 0; + } else if (con->current.border == B_CSD) { + border_top = 0; + border_width = 0; + } else { + sway_assert(false, "unreachable"); + } + + int border_bottom = con->current.border_bottom ? border_width : 0; + int border_left = con->current.border_left ? border_width : 0; + int border_right = con->current.border_right ? border_width : 0; + int vert_border_height = MAX(0, height - border_top - border_bottom); + + wlr_scene_rect_set_size(con->border.top, width, border_top + corner_radius); + wlr_scene_rect_set_size(con->border.bottom, width, border_bottom + corner_radius); + wlr_scene_rect_set_size(con->border.left, + border_left, vert_border_height - vert_border_offset - corner_radius); + wlr_scene_rect_set_size(con->border.right, + border_right, vert_border_height - vert_border_offset - corner_radius); + + wlr_scene_rect_set_corner_radius(con->border.top, !has_corner_radius ? 0 : + corner_radius + border_width, CORNER_LOCATION_TOP); + wlr_scene_rect_set_corner_radius(con->border.bottom, !has_corner_radius ? 0 : + corner_radius + border_width, CORNER_LOCATION_BOTTOM); + + wlr_scene_rect_set_clipped_region(con->border.top, (struct clipped_region) { + .corner_radius = corner_radius, + .corners = CORNER_LOCATION_TOP, + .area = { + .x = border_width, + .y = border_width, + .width = width - 2 * border_width, + .height = border_top + corner_radius + } + }); + wlr_scene_rect_set_clipped_region(con->border.bottom, (struct clipped_region) { + .corner_radius = corner_radius, + .corners = CORNER_LOCATION_BOTTOM, + .area = { + .x = border_width, + .y = 0, + .width = width - 2 * border_width, + .height = border_bottom - border_width + corner_radius, + } + }); + + wlr_scene_node_set_position(&con->border.top->node, 0, 0); + wlr_scene_node_set_position(&con->border.bottom->node, + 0, height - border_bottom - corner_radius); + wlr_scene_node_set_position(&con->border.left->node, + 0, border_top + vert_border_offset); + wlr_scene_node_set_position(&con->border.right->node, + width - border_right, border_top + vert_border_offset); + + // Dim + if (con->dim_rect) { + wlr_scene_node_set_position(&con->dim_rect->node, border_left, border_top); + wlr_scene_rect_set_size(con->dim_rect, con->current.content_width, + con->current.content_height); + bool has_titlebar = !title_bar || con->current.border == B_NORMAL; + wlr_scene_rect_set_corner_radius(con->dim_rect, con->corner_radius, + has_titlebar ? CORNER_LOCATION_BOTTOM : CORNER_LOCATION_ALL); + } + + // make sure to reparent, it's possible that the client just came out of + // fullscreen mode where the parent of the surface is not the container + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + wlr_scene_node_set_position(&con->view->scene_tree->node, border_left, border_top); + } else { + // make sure to disable the title bar if the parent is not managing it + if (title_bar) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + } + + wlr_scene_node_set_enabled(&con->shadow->node, + container_has_shadow(con) && + (con->current.layout == L_TABBED || con->current.layout == L_STACKED)); + + arrange_children(con->current.layout, con->current.children, + con->current.focused_inactive_child, con->content_tree, + width, height, gaps); + } +} + +static int container_get_gaps(struct sway_container *con) { + struct sway_workspace *ws = con->current.workspace; + struct sway_container *temp = con; + while (temp) { + enum sway_container_layout layout; + if (temp->current.parent) { + layout = temp->current.parent->current.layout; + } else { + layout = ws->current.layout; + } + if (layout == L_TABBED || layout == L_STACKED) { + return 0; + } + temp = temp->pending.parent; + } + return ws->gaps_inner; +} + +static void arrange_fullscreen(struct wlr_scene_tree *tree, + struct sway_container *fs, struct sway_workspace *ws, + int width, int height) { + struct wlr_scene_node *fs_node; + if (fs->view) { + fs_node = &fs->view->scene_tree->node; + + // if we only care about the view, disable any decorations + wlr_scene_node_set_enabled(&fs->scene_tree->node, false); + } else { + fs_node = &fs->scene_tree->node; + arrange_container(fs, width, height, true, container_get_gaps(fs)); + } + + wlr_scene_node_reparent(fs_node, tree); + wlr_scene_node_lower_to_bottom(fs_node); + wlr_scene_node_set_position(fs_node, 0, 0); +} + +static void arrange_workspace_floating(struct sway_workspace *ws) { + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + struct wlr_scene_tree *layer = root->layers.floating; + + if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { + continue; + } + + if (root->fullscreen_global) { + if (container_is_transient_for(floater, root->fullscreen_global)) { + layer = root->layers.fullscreen_global; + } + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *active = output->current.active_workspace; + + if (active && active->fullscreen && + container_is_transient_for(floater, active->fullscreen)) { + layer = root->layers.fullscreen; + } + } + } + + wlr_scene_node_reparent(&floater->scene_tree->node, layer); + wlr_scene_node_set_position(&floater->scene_tree->node, + floater->current.x, floater->current.y); + wlr_scene_node_set_enabled(&floater->scene_tree->node, true); + wlr_scene_node_set_enabled(&floater->shadow->node, container_has_shadow(floater) && floater->view); + + arrange_container(floater, floater->current.width, floater->current.height, + true, ws->gaps_inner); + } +} + +static void arrange_workspace_tiling(struct sway_workspace *ws, + int width, int height) { + arrange_children(ws->current.layout, ws->current.tiling, + ws->current.focused_inactive_child, ws->layers.tiling, + width, height, ws->gaps_inner); +} + +static void disable_workspace(struct sway_workspace *ws) { + // if any containers were just moved to a disabled workspace it will + // have the parent of the old workspace. Move the workspace so that it won't + // be shown. + for (int i = 0; i < ws->current.tiling->length; i++) { + struct sway_container *child = ws->current.tiling->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); + disable_container(child); + } + + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + disable_container(floater); + wlr_scene_node_set_enabled(&floater->scene_tree->node, false); + } +} + +static void arrange_output(struct sway_output *output, int width, int height) { + for (int i = 0; i < output->current.workspaces->length; i++) { + struct sway_workspace *child = output->current.workspaces->items[i]; + + bool activated = output->current.active_workspace == child && output->wlr_output->enabled; + + wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); + wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); + + for (int i = 0; i < child->current.floating->length; i++) { + struct sway_container *floater = child->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); + } + + if (activated) { + struct sway_container *fs = child->current.fullscreen; + wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); + + arrange_workspace_floating(child); + + wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&output->layers.blur_layer->node, !fs); + wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); + + if (fs) { + wlr_scene_rect_set_size(output->fullscreen_background, width, height); + + arrange_fullscreen(child->layers.fullscreen, fs, child, + width, height); + } else { + struct wlr_box *area = &output->usable_area; + struct side_gaps *gaps = &child->current_gaps; + + wlr_scene_node_set_position(&child->layers.tiling->node, + gaps->left + area->x, gaps->top + area->y); + + arrange_workspace_tiling(child, + area->width - gaps->left - gaps->right, + area->height - gaps->top - gaps->bottom); + } + } else { + wlr_scene_node_set_enabled(&child->layers.tiling->node, false); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); + + disable_workspace(child); + } + } +} + +void arrange_popups(struct wlr_scene_tree *popups) { + struct wlr_scene_node *node; + wl_list_for_each(node, &popups->children, link) { + struct sway_popup_desc *popup = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_POPUP); + + if (popup) { + int lx, ly; + wlr_scene_node_coords(popup->relative, &lx, &ly); + wlr_scene_node_set_position(node, lx, ly); + } + } +} + +static void arrange_root(struct sway_root *root) { + struct sway_container *fs = root->fullscreen_global; + + wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&root->layers.blur_tree->node, !fs); + wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); + wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); + + // hide all contents in the scratchpad + for (int i = 0; i < root->scratchpad->length; i++) { + struct sway_container *con = root->scratchpad->items[i]; + + // When a container is moved to a scratchpad, it's possible that it + // was moved into a floating container as part of the same transaction. + // In this case, we need to make sure we reparent all the container's + // children so that disabling the container will disable all descendants. + if (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) { + struct sway_container *child = con->current.children->items[ii]; + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + } + + wlr_scene_node_set_enabled(&con->scene_tree->node, false); + } + + if (fs) { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *ws = output->current.active_workspace; + + if (ws) { + arrange_workspace_floating(ws); + } + } + + arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, + root->width, root->height); + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + + wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + + wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); + wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); + wlr_scene_node_reparent(&output->layers.blur_layer->node, root->layers.blur_tree); + wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); + wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); + wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); + wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); + wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); + + wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.blur_layer->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); + + arrange_output(output, output->width, output->height); } } - // If the view hasn't responded to the configure, center it within - // the container. This is important for fullscreen views which - // refuse to resize to the size of the output. - if (view && view->surface) { - view_center_surface(view); - } - - // Damage the new location - desktop_damage_whole_container(container); - if (view && view->surface) { - struct wlr_surface *surface = view->surface; - struct wlr_box box = { - .x = container->current.content_x - view->geometry.x, - .y = container->current.content_y - view->geometry.y, - .width = surface->current.width, - .height = surface->current.height, - }; - desktop_damage_box(&box); - } - - if (!container->node.destroying) { - container_discover_outputs(container); - } + arrange_popups(root->layers.popup); } /** @@ -326,8 +823,6 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - - cursor_rebase_all(); } static void transaction_commit_pending(void); @@ -340,6 +835,8 @@ static void transaction_progress(void) { return; } transaction_apply(server.queued_transaction); + arrange_root(root); + cursor_rebase_all(); transaction_destroy(server.queued_transaction); server.queued_transaction = NULL; @@ -373,7 +870,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) { @@ -415,21 +912,11 @@ static void transaction_commit(struct sway_transaction *transaction) { ++transaction->num_waiting; } - // From here on we are rendering a saved buffer of the view, which - // means we can send a frame done event to make the client redraw it - // as soon as possible. Additionally, this is required if a view is - // mapping and its default geometry doesn't intersect an output. - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done( - node->sway_container->view->surface, &now); + view_send_frame_done(node->sway_container->view); } if (!hidden && node_is_view(node) && - wl_list_empty(&node->sway_container->view->saved_buffers)) { + !node->sway_container->view->saved_surface_tree) { view_save_buffer(node->sway_container->view); - memcpy(&node->sway_container->view->saved_geometry, - &node->sway_container->view->geometry, - sizeof(struct wlr_box)); } node->instruction = instruction; } @@ -499,16 +986,18 @@ static void set_instruction_ready( transaction_progress(); } -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial) { struct sway_transaction_instruction *instruction = view->container->node.instruction; if (instruction != NULL && instruction->serial == serial) { set_instruction_ready(instruction); + return true; } + return false; } -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; @@ -518,7 +1007,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, instruction->container_state.content_width == width && instruction->container_state.content_height == height) { set_instruction_ready(instruction); + return true; } + return false; } static void _transaction_commit_dirty(bool server_request) { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 6dde98bd..0d4387fa 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 199309L #include #include #include @@ -7,7 +6,7 @@ #include #include "log.h" #include "sway/decoration.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -19,52 +18,30 @@ #include "sway/tree/workspace.h" #include "sway/xdg_decoration.h" -static const struct sway_view_child_impl popup_impl; - -static void popup_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - - wlr_xdg_popup_get_toplevel_coords(wlr_popup, - wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, - wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, - sx, sy); -} - -static void popup_destroy(struct sway_view_child *child) { - if (!sway_assert(child->impl == &popup_impl, - "Expected an xdg_shell popup")) { - return; - } - struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - wl_list_remove(&popup->new_popup.link); - wl_list_remove(&popup->destroy.link); - free(popup); -} - -static const struct sway_view_child_impl popup_impl = { - .get_view_coords = popup_get_view_coords, - .destroy = popup_destroy, -}; - static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view); + struct wlr_xdg_popup *wlr_popup, struct sway_view *view, + struct wlr_scene_tree *parent); static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->child.view); + popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); - view_child_destroy(&popup->child); + + wl_list_remove(&popup->new_popup.link); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->surface_commit.link); + wl_list_remove(&popup->reposition.link); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); } static void popup_unconstrain(struct sway_xdg_popup *popup) { - struct sway_view *view = popup->child.view; + struct sway_view *view = popup->view; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; struct sway_workspace *workspace = view->container->pending.workspace; @@ -87,32 +64,72 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); } -static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { +static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { + struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); + if (popup->wlr_xdg_popup->base->initial_commit) { + popup_unconstrain(popup); + } +} + +static void popup_handle_reposition(struct wl_listener *listener, void *data) { + struct sway_xdg_popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + +static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, + struct sway_view *view, struct wlr_scene_tree *parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; - struct sway_xdg_popup *popup = - calloc(1, sizeof(struct sway_xdg_popup)); - if (popup == NULL) { + struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); + if (!popup) { return NULL; } - view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); - popup->wlr_xdg_popup = xdg_surface->popup; + popup->wlr_xdg_popup = wlr_popup; + popup->view = view; + + popup->scene_tree = wlr_scene_tree_create(parent); + if (!popup->scene_tree) { + free(popup); + return NULL; + } + + popup->xdg_surface_tree = wlr_scene_xdg_surface_create( + popup->scene_tree, xdg_surface); + if (!popup->xdg_surface_tree) { + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return NULL; + } + + popup->desc.relative = &view->content_tree->node; + popup->desc.view = view; + + if (!scene_descriptor_assign(&popup->scene_tree->node, + SWAY_SCENE_DESC_POPUP, &popup->desc)) { + sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return NULL; + } + + popup->wlr_xdg_popup = xdg_surface->popup; + struct sway_xdg_shell_view *shell_view = + wl_container_of(view, shell_view, view); + xdg_surface->data = shell_view; + + wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); + popup->surface_commit.notify = popup_handle_surface_commit; wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; - wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); + popup->reposition.notify = popup_handle_reposition; + wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->surface->events.unmap, &popup->child.surface_unmap); - - popup_unconstrain(popup); - return popup; } - static struct sway_xdg_shell_view *xdg_shell_view_from_view( struct sway_view *view) { if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, @@ -207,24 +224,6 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } -static void for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, - user_data); -} - -static void for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, - iterator, user_data); -} - static bool is_transient_for(struct sway_view *child, struct sway_view *ancestor) { if (xdg_shell_view_from_view(child) == NULL) { @@ -272,8 +271,6 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .set_resizing = set_resizing, .wants_floating = wants_floating, - .for_each_surface = for_each_surface, - .for_each_popup_surface = for_each_popup_surface, .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, @@ -286,6 +283,22 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; + if (xdg_surface->initial_commit) { + if (view->xdg_decoration != NULL) { + set_xdg_decoration_mode(view->xdg_decoration); + } + // 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; + } + + if (!xdg_surface->surface->mapped) { + return; + } + struct wlr_box new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); bool new_size = new_geo.width != view->geometry.width || @@ -297,7 +310,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); @@ -307,18 +319,23 @@ static void handle_commit(struct wl_listener *listener, void *data) { view->geometry.height); } transaction_commit_dirty_client(); - } else { - view_center_surface(view); } - desktop_damage_view(view); + + view_center_and_clip_surface(view); } if (view->container->node.instruction) { - transaction_notify_view_ready_by_serial(view, + bool successful = transaction_notify_view_ready_by_serial(view, xdg_surface->current.configure_serial); - } - view_damage_from(view); + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } + } } static void handle_set_title(struct wl_listener *listener, void *data) { @@ -333,6 +350,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, set_app_id); struct sway_view *view = &xdg_shell_view->view; + view_update_app_id(view); view_execute_criteria(view); } @@ -340,7 +358,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, &xdg_shell_view->view); + + struct sway_xdg_popup *popup = popup_create(wlr_popup, + &xdg_shell_view->view, root->layers.popup); + if (!popup) { + return; + } + + int lx, ly; + wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); } static void handle_request_maximize(struct wl_listener *listener, void *data) { @@ -350,7 +377,6 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) { wlr_xdg_surface_schedule_configure(toplevel->base); } -/* Minimize to scratchpad */ static void handle_request_minimize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_minimize); @@ -439,7 +465,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); - wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->request_maximize.link); if (xdg_shell_view->request_minimize.notify) { @@ -485,10 +510,6 @@ static void handle_map(struct wl_listener *listener, void *data) { transaction_commit_dirty(); - xdg_shell_view->commit.notify = handle_commit; - wl_signal_add(&toplevel->base->surface->events.commit, - &xdg_shell_view->commit); - xdg_shell_view->new_popup.notify = handle_new_popup; wl_signal_add(&toplevel->base->events.new_popup, &xdg_shell_view->new_popup); @@ -534,6 +555,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); + wl_list_remove(&xdg_shell_view->commit.link); view->wlr_xdg_toplevel = NULL; if (view->xdg_decoration) { view->xdg_decoration->view = NULL; @@ -546,17 +568,12 @@ struct sway_view *view_from_wlr_xdg_surface( return xdg_surface->data; } -void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { - struct wlr_xdg_surface *xdg_surface = data; - - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - sway_log(SWAY_DEBUG, "New xdg_shell popup"); - return; - } +void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { + struct wlr_xdg_toplevel *xdg_toplevel = data; sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", - xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); - wlr_xdg_surface_ping(xdg_surface); + xdg_toplevel->title, xdg_toplevel->app_id); + wlr_xdg_surface_ping(xdg_toplevel->base); struct sway_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct sway_xdg_shell_view)); @@ -564,17 +581,26 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { return; } - view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); - xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; + if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { + free(xdg_shell_view); + return; + } + xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; xdg_shell_view->map.notify = handle_map; - wl_signal_add(&xdg_surface->surface->events.map, &xdg_shell_view->map); + wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); xdg_shell_view->unmap.notify = handle_unmap; - wl_signal_add(&xdg_surface->surface->events.unmap, &xdg_shell_view->unmap); + wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); + + xdg_shell_view->commit.notify = handle_commit; + wl_signal_add(&xdg_toplevel->base->surface->events.commit, + &xdg_shell_view->commit); xdg_shell_view->destroy.notify = handle_destroy; - wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); + wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); - xdg_surface->data = xdg_shell_view; + wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); + + xdg_toplevel->base->data = xdg_shell_view; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 709795e8..f736f5f9 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -1,5 +1,5 @@ -#define _POSIX_C_SOURCE 199309L #include +#include #include #include #include @@ -9,12 +9,12 @@ #include #include #include "log.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/server.h" @@ -45,29 +45,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, ev->width, ev->height); } -static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, commit); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - false); -} - static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, set_geometry); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - if (xsurface->x != surface->lx || xsurface->y != surface->ly) { - // Surface has moved - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - } + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); } static void unmanaged_handle_map(struct wl_listener *listener, void *data) { @@ -75,17 +58,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { wl_container_of(listener, surface, map); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); + surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, + xsurface->surface); - wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); - surface->set_geometry.notify = unmanaged_handle_set_geometry; + if (surface->surface_scene) { + scene_descriptor_assign(&surface->surface_scene->buffer->node, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, + xsurface->x, xsurface->y); - wl_signal_add(&xsurface->surface->events.commit, &surface->commit); - surface->commit.notify = unmanaged_handle_commit; - - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); + wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); + surface->set_geometry.notify = unmanaged_handle_set_geometry; + } if (wlr_xwayland_or_surface_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); @@ -99,10 +83,13 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, unmap); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); - wl_list_remove(&surface->link); - wl_list_remove(&surface->set_geometry.link); - wl_list_remove(&surface->commit.link); + + if (surface->surface_scene) { + wl_list_remove(&surface->set_geometry.link); + + wlr_scene_node_destroy(&surface->surface_scene->buffer->node); + surface->surface_scene = NULL; + } struct sway_seat *seat = input_manager_current_seat(); if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { @@ -302,7 +289,9 @@ static void set_activated(struct sway_view *view, bool activated) { } wlr_xwayland_surface_activate(surface, activated); - wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + if (activated) { + wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + } } static void set_tiled(struct sway_view *view, bool tiled) { @@ -426,17 +415,6 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; -static void get_geometry(struct sway_view *view, struct wlr_box *box) { - box->x = box->y = 0; - if (view->surface) { - box->width = view->surface->current.width; - box->height = view->surface->current.height; - } else { - box->width = 0; - box->height = 0; - } -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); @@ -444,34 +422,38 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; struct wlr_surface_state *state = &xsurface->surface->current; - struct wlr_box new_geo; - get_geometry(view, &new_geo); + struct wlr_box new_geo = {0}; + new_geo.width = state->width; + new_geo.height = state->height; + bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height || - new_geo.x != view->geometry.x || - new_geo.y != view->geometry.y; + new_geo.height != view->geometry.height; if (new_size) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); transaction_commit_dirty_client(); - } else { - view_center_surface(view); } - desktop_damage_view(view); + + view_center_and_clip_surface(view); } if (view->container->node.instruction) { - transaction_notify_view_ready_by_geometry(view, + bool successful = transaction_notify_view_ready_by_geometry(view, xsurface->x, xsurface->y, state->width, state->height); - } - view_damage_from(view); + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } + } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -515,9 +497,21 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - view_unmap(view); - wl_list_remove(&xwayland_view->commit.link); + wl_list_remove(&xwayland_view->surface_tree_destroy.link); + + if (xwayland_view->surface_tree) { + wlr_scene_node_destroy(&xwayland_view->surface_tree->node); + xwayland_view->surface_tree = NULL; + } + + view_unmap(view); +} + +static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, + surface_tree_destroy); + xwayland_view->surface_tree = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -537,6 +531,15 @@ static void handle_map(struct wl_listener *listener, void *data) { // Put it back into the tree view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); + xwayland_view->surface_tree = wlr_scene_subsurface_tree_create( + xwayland_view->view.content_tree, xsurface->surface); + + if (xwayland_view->surface_tree) { + xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy; + wl_signal_add(&xwayland_view->surface_tree->node.events.destroy, + &xwayland_view->surface_tree_destroy); + } + transaction_commit_dirty(); } @@ -613,7 +616,6 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } -/* Minimize to scratchpad */ static void handle_request_minimize(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, request_minimize); @@ -645,6 +647,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { transaction_commit_dirty(); return; } + struct sway_seat *seat = input_manager_current_seat(); bool focused = seat_get_focus(seat) == &view->container->node; wlr_xwayland_surface_set_minimized(xsurface, !focused && e->minimize); @@ -818,7 +821,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu return NULL; } - view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); + if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { + free(xwayland_view); + return NULL; + } xwayland_view->view.wlr_xwayland_surface = xsurface; wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fa9d5b33..235951d4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,12 +19,13 @@ #include "log.h" #include "util.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/tablet.h" #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" @@ -37,65 +38,6 @@ static uint32_t get_current_time_msec(void) { return now.tv_sec * 1000 + now.tv_nsec / 1000000; } -static struct wlr_surface *input_popup_surface_at(struct sway_output *output, - struct sway_input_method_relay *relay, double ox, double oy, double *sx, double *sy) { - struct sway_input_popup *popup; - wl_list_for_each(popup, &relay->input_popups, link) { - int lx, ly; - if (!sway_input_popup_get_position(popup, &lx, &ly)) { - continue; - } - if (!popup->visible) { - continue; - } - double _sx = ox - lx; - double _sy = oy - ly; - struct wlr_surface *sub = wlr_surface_surface_at( - popup->popup_surface->surface, _sx, _sy, sx, sy); - if (sub) { - return sub; - } - } - return NULL; -} - -static struct wlr_surface *layer_surface_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub) { - return sub; - } - } - return NULL; -} - -static bool surface_is_xdg_popup(struct wlr_surface *surface) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface != NULL && xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && - xdg_surface->popup != NULL; -} - -static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub && surface_is_xdg_popup(sub)) { - return sub; - } - } - return NULL; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -103,144 +45,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - // find the output the cursor is on + struct wlr_scene_node *scene_node = NULL; + + struct wlr_scene_node *node; + wl_list_for_each_reverse(node, &root->layer_tree->children, link) { + struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node); + + bool non_interactive = scene_descriptor_try_get(&layer->node, + SWAY_SCENE_DESC_NON_INTERACTIVE); + if (non_interactive) { + continue; + } + + scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); + if (scene_node) { + break; + } + } + + if (scene_node) { + // determine what wlr_surface we clicked on + if (scene_node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(scene_node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + + if (scene_surface) { + *surface = scene_surface->surface; + } + } + + // determine what container we clicked on + struct wlr_scene_node *current = scene_node; + while (true) { + struct sway_container *con = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_CONTAINER); + + if (!con) { + struct sway_view *view = scene_descriptor_try_get(current, + SWAY_SCENE_DESC_VIEW); + if (view) { + con = view->container; + } + } + + if (!con) { + struct sway_popup_desc *popup = + scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); + if (popup && popup->view) { + con = popup->view->container; + } + } + + if (con && (!con->view || con->view->surface)) { + return &con->node; + } + + if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) { + // We don't want to feed through the current workspace on + // layer shells + return NULL; + } + +#if WLR_HAS_XWAYLAND + if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { + return NULL; + } +#endif + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } + } + + // if we aren't on a container, determine what workspace we are on struct wlr_output *wlr_output = wlr_output_layout_output_at( root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } + struct sway_output *output = wlr_output->data; if (!output || !output->enabled) { // output is being destroyed or is being enabled return NULL; } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return NULL; - } - struct wlr_session_lock_surface_v1 *lock_surf; - wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { - if (lock_surf->output != wlr_output) { - continue; - } - - *surface = wlr_surface_surface_at(lock_surf->surface, ox, oy, sx, sy); - if (*surface != NULL) { - return NULL; - } - } - return NULL; - } - - // layer surfaces on the overlay layer are rendered on top - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy))) { - return NULL; - } - - if ((*surface = input_popup_surface_at(output, - &seat->im_relay, ox, oy, sx, sy))) { - struct sway_cursor* cursor = seat->cursor; - // set cursot to left_ptr - cursor_set_image(cursor, "left_ptr", NULL); - return NULL; - } - // check for unmanaged views -#if HAVE_XWAYLAND - struct wl_list *unmanaged = &root->xwayland_unmanaged; - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - - double _sx = lx - unmanaged_surface->lx; - double _sy = ly - unmanaged_surface->ly; - if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { - *surface = xsurface->surface; - *sx = _sx; - *sy = _sy; - return NULL; - } - } -#endif - - if (root->fullscreen_global) { - // Try fullscreen container - struct sway_container *con = tiling_container_at( - &root->fullscreen_global->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - - // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); if (!ws) { return NULL; } - if (ws->fullscreen) { - // Try transient containers - for (int i = 0; i < ws->floating->length; ++i) { - struct sway_container *floater = ws->floating->items[i]; - if (container_is_transient_for(floater, ws->fullscreen)) { - struct sway_container *con = tiling_container_at( - &floater->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - } - } - // Try fullscreen container - struct sway_container *con = - tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - - struct sway_container *c; - if ((c = container_at(ws, lx, ly, surface, sx, sy))) { - return &c->node; - } - - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - return &ws->node; } @@ -345,7 +244,7 @@ static enum sway_input_idle_source idle_source_from_device( return IDLE_SOURCE_POINTER; case WLR_INPUT_DEVICE_TOUCH: return IDLE_SOURCE_TOUCH; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: return IDLE_SOURCE_TABLET_TOOL; case WLR_INPUT_DEVICE_TABLET_PAD: return IDLE_SOURCE_TABLET_PAD; @@ -458,7 +357,7 @@ static void handle_pointer_motion_absolute( void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { if (time_msec == 0) { time_msec = get_current_time_msec(); } @@ -470,7 +369,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct wlr_pointer_button_event *event = data; - if (event->state == WLR_BUTTON_PRESSED) { + if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { cursor->pressed_button_count++; } else { if (cursor->pressed_button_count > 0) { @@ -532,7 +431,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { cursor->pointer_touch_up = true; dispatch_cursor_button(cursor, &event->touch->base, - event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); + event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); } } else { seatop_touch_up(seat, event); @@ -550,7 +449,7 @@ static void handle_touch_cancel(struct wl_listener *listener, void *data) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { cursor->pointer_touch_up = true; dispatch_cursor_button(cursor, &event->touch->base, - event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); + event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); } } else { seatop_touch_cancel(seat, event); @@ -572,12 +471,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { if (seat->touch_id == event->touch_id) { seat->touch_x = lx; seat->touch_y = ly; - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + + drag_icons_update_position(seat); } if (cursor->simulating_pointer_from_touch) { @@ -624,7 +519,7 @@ static void apply_mapping_from_region(struct wlr_input_device *device, double x1 = region->x1, x2 = region->x2; double y1 = region->y1, y2 = region->y2; - if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET) { struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); if (tablet->width_mm == 0 || tablet->height_mm == 0) { return; @@ -767,7 +662,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { event->state == WLR_TABLET_TOOL_TIP_UP) { cursor->simulating_pointer_from_tool_tip = false; dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, - BTN_LEFT, WLR_BUTTON_RELEASED); + BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { // If we started holding the tool tip down on a surface that accepts @@ -779,7 +674,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { } else { cursor->simulating_pointer_from_tool_tip = true; dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); + event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } } else { @@ -882,13 +777,13 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { case WLR_BUTTON_PRESSED: if (cursor->tool_buttons == 0) { dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_RIGHT, event->state); + event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); } break; case WLR_BUTTON_RELEASED: if (cursor->tool_buttons <= 1) { dispatch_cursor_button(cursor, &event->tablet->base, - event->time_msec, BTN_RIGHT, event->state); + event->time_msec, BTN_RIGHT, WL_POINTER_BUTTON_STATE_RELEASED); } break; } @@ -1377,8 +1272,7 @@ const char *get_mouse_button_name(uint32_t button) { static void warp_to_constraint_cursor_hint(struct sway_cursor *cursor) { struct wlr_pointer_constraint_v1 *constraint = cursor->active_constraint; - if (constraint->current.committed & - WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { + if (constraint->current.cursor_hint.enabled) { double sx = constraint->current.cursor_hint.x; double sy = constraint->current.cursor_hint.y; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 288fddc4..ddfe1468 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -1,12 +1,11 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include #include +#include #include #include #include -#include #include #include #include "sway/config.h" @@ -67,8 +66,15 @@ struct sway_seat *input_manager_sway_seat_from_wlr_seat(struct wlr_seat *wlr_sea } char *input_device_get_identifier(struct wlr_input_device *device) { - int vendor = device->vendor; - int product = device->product; + int vendor = 0, product = 0; +#if WLR_HAS_LIBINPUT_BACKEND + if (wlr_input_device_is_libinput(device)) { + struct libinput_device *libinput_dev = wlr_libinput_get_device_handle(device); + vendor = libinput_device_get_id_vendor(libinput_dev); + product = libinput_device_get_id_product(libinput_dev); + } +#endif + char *name = strdup(device->name ? device->name : ""); strip_whitespace(name); @@ -113,7 +119,7 @@ const char *input_device_get_type(struct sway_input_device *device) { return "keyboard"; case WLR_INPUT_DEVICE_TOUCH: return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: return "tablet_tool"; case WLR_INPUT_DEVICE_TABLET_PAD: return "tablet_pad"; @@ -284,34 +290,6 @@ static void handle_new_input(struct wl_listener *listener, void *data) { } } -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; - if (server.session_lock.locked) { - // Don't deactivate the grab of a screenlocker - return; - } - wl_list_for_each(seat, &input_manager->seats, link) { - seat_set_exclusive_client(seat, NULL); - struct sway_node *previous = seat_get_focus(seat); - if (previous) { - // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, NULL); - seat_set_focus(seat, previous); - } - } -} - static void handle_keyboard_shortcuts_inhibitor_destroy( struct wl_listener *listener, void *data) { struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = @@ -454,6 +432,20 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) { } } +static void handle_transient_seat_manager_create_seat( + struct wl_listener *listener, void *data) { + struct wlr_transient_seat_v1 *transient_seat = data; + static uint64_t i; + char name[256]; + snprintf(name, sizeof(name), "transient-%"PRIx64, i++); + struct sway_seat *seat = seat_create(name); + if (seat && seat->wlr_seat) { + wlr_transient_seat_v1_ready(transient_seat, seat->wlr_seat); + } else { + wlr_transient_seat_v1_deny(transient_seat); + } +} + struct sway_input_manager *input_manager_create(struct sway_server *server) { struct sway_input_manager *input = calloc(1, sizeof(struct sway_input_manager)); @@ -480,14 +472,6 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { &input->virtual_pointer_new); input->virtual_pointer_new.notify = handle_virtual_pointer; - 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); - input->keyboard_shortcuts_inhibit = wlr_keyboard_shortcuts_inhibit_v1_create(server->wl_display); input->keyboard_shortcuts_inhibit_new_inhibitor.notify = @@ -497,6 +481,15 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); + input->transient_seat_manager = + wlr_transient_seat_manager_v1_create(server->wl_display); + assert(input->transient_seat_manager); + + input->transient_seat_create.notify = + handle_transient_seat_manager_create_seat; + wl_signal_add(&input->transient_seat_manager->events.create_seat, + &input->transient_seat_create); + return input; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 8927287f..1a73df01 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 @@ -32,6 +33,7 @@ static struct modifier_key { { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, { "Mod3", WLR_MODIFIER_MOD3 }, { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, + { "Super", WLR_MODIFIER_LOGO }, { "Mod5", WLR_MODIFIER_MOD5 }, }; @@ -405,8 +407,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, char *device_identifier = input_device_get_identifier(wlr_device); bool exact_identifier = keyboard->wlr->group != NULL; seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); - bool input_inhibited = seat->exclusive_client != NULL || - server.session_lock.locked; + bool locked = server.session_lock.lock; struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; @@ -424,17 +425,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, struct sway_binding *binding_released = NULL; get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, - keyinfo.code_modifiers, true, input_inhibited, + keyinfo.code_modifiers, true, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding_released, - keyinfo.raw_modifiers, true, input_inhibited, + keyinfo.raw_modifiers, true, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding_released, - keyinfo.translated_modifiers, true, input_inhibited, + keyinfo.translated_modifiers, true, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); @@ -456,17 +457,17 @@ static void handle_key_event(struct sway_keyboard *keyboard, if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, - keyinfo.code_modifiers, false, input_inhibited, + keyinfo.code_modifiers, false, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding, - keyinfo.raw_modifiers, false, input_inhibited, + keyinfo.raw_modifiers, false, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, - keyinfo.translated_modifiers, false, input_inhibited, + keyinfo.translated_modifiers, false, locked, shortcuts_inhibited, device_identifier, exact_identifier, keyboard->effective_layout); } @@ -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); } @@ -1052,6 +1028,49 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { } } + 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); + } + // If the seat has no active keyboard, set this one struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; @@ -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..5754fa69 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, @@ -395,8 +410,8 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { } const char prefix_platform[] = "platform-"; - if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { - return false; + if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) == 0) { + return true; } const char prefix_pci[] = "pci-"; diff --git a/sway/input/seat.c b/sway/input/seat.c index 43289598..0dd26290 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -18,7 +17,7 @@ #include "list.h" #include "log.h" #include "sway/config.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" @@ -68,6 +67,12 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) { } void seat_destroy(struct sway_seat *seat) { + wlr_seat_destroy(seat->wlr_seat); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, destroy); + if (seat == config->handler_context.seat) { config->handler_context.seat = input_manager_get_default_seat(); } @@ -88,10 +93,11 @@ void seat_destroy(struct sway_seat *seat) { wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); - wlr_seat_destroy(seat->wlr_seat); + wl_list_remove(&seat->destroy.link); for (int i = 0; i < seat->deferred_bindings->length; i++) { free_sway_binding(seat->deferred_bindings->items[i]); } + wlr_scene_node_destroy(&seat->scene_tree->node); list_free(seat->deferred_bindings); free(seat->prev_workspace_name); free(seat); @@ -184,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); @@ -353,25 +359,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { seat_node_from_node(seat, node); } -static void drag_icon_damage_whole(struct sway_drag_icon *icon) { - if (!icon->wlr_drag_icon->surface->mapped) { - return; - } - desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); -} - -void drag_icon_update_position(struct sway_drag_icon *icon) { - drag_icon_damage_whole(icon); - - struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - struct sway_seat *seat = icon->seat; +static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) { + struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON); struct wlr_cursor *cursor = seat->cursor->cursor; + switch (wlr_icon->drag->grab_type) { case WLR_DRAG_GRAB_KEYBOARD: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x + icon->dx; - icon->y = cursor->y + icon->dy; + wlr_scene_node_set_position(node, cursor->x, cursor->y); break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -379,42 +375,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + icon->dx; - icon->y = seat->touch_y + icon->dy; + wlr_scene_node_set_position(node, seat->touch_x, seat->touch_y); } - - drag_icon_damage_whole(icon); } -static void drag_icon_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_drag_icon *icon = - wl_container_of(listener, icon, surface_commit); - struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - icon->dx += wlr_icon->surface->current.dx; - icon->dy += wlr_icon->surface->current.dy; - drag_icon_update_position(icon); -} - -static void drag_icon_handle_map(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, map); - drag_icon_damage_whole(icon); -} - -static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); - drag_icon_damage_whole(icon); -} - -static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); - icon->wlr_drag_icon->data = NULL; - wl_list_remove(&icon->link); - wl_list_remove(&icon->surface_commit.link); - wl_list_remove(&icon->unmap.link); - wl_list_remove(&icon->map.link); - wl_list_remove(&icon->destroy.link); - free(icon); +void drag_icons_update_position(struct sway_seat *seat) { + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + drag_icon_update_position(seat, node); + } } static void drag_handle_destroy(struct wl_listener *listener, void *data) { @@ -486,27 +455,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; if (wlr_drag_icon != NULL) { - struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); - if (icon == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); + struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); return; } - icon->seat = seat; - icon->wlr_drag_icon = wlr_drag_icon; - wlr_drag_icon->data = icon; - icon->surface_commit.notify = drag_icon_handle_surface_commit; - wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); - icon->unmap.notify = drag_icon_handle_unmap; - wl_signal_add(&wlr_drag_icon->surface->events.unmap, &icon->unmap); - icon->map.notify = drag_icon_handle_map; - wl_signal_add(&wlr_drag_icon->surface->events.map, &icon->map); - icon->destroy.notify = drag_icon_handle_destroy; - wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); + if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON, + wlr_drag_icon)) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor"); + wlr_scene_node_destroy(&tree->node); + return; + } - wl_list_insert(&root->drag_icons, &icon->link); - - drag_icon_update_position(icon); + drag_icon_update_position(seat, &tree->node); } seatop_begin_default(seat); } @@ -553,8 +515,18 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } + bool failed = false; + seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed); + seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed); + if (failed) { + wlr_scene_node_destroy(&seat->scene_tree->node); + free(seat); + return NULL; + } + seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { + wlr_scene_node_destroy(&seat->scene_tree->node); free(seat); return NULL; } @@ -562,11 +534,15 @@ struct sway_seat *seat_create(const char *seat_name) { seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { + wlr_scene_node_destroy(&seat->scene_tree->node); wlr_seat_destroy(seat->wlr_seat); free(seat); return NULL; } + seat->destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy); + seat->idle_inhibit_sources = seat->idle_wake_sources = IDLE_SOURCE_KEYBOARD | IDLE_SOURCE_POINTER | @@ -640,7 +616,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { case WLR_INPUT_DEVICE_TOUCH: caps |= WL_SEAT_CAPABILITY_TOUCH; break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: caps |= WL_SEAT_CAPABILITY_POINTER; break; case WLR_INPUT_DEVICE_SWITCH: @@ -698,7 +674,7 @@ static const char *get_builtin_output_name(void) { static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { switch (seat_device->input_device->wlr_device->type) { case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: return true; default: return false; @@ -713,7 +689,7 @@ static void seat_apply_input_mapping(struct sway_seat *seat, switch (sway_device->input_device->wlr_device->type) { case WLR_INPUT_DEVICE_POINTER: case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: break; default: return; // these devices don't support mappings @@ -826,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); } } @@ -906,7 +881,7 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TOUCH: seat_configure_touch(seat, seat_device); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: seat_configure_tablet_tool(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: @@ -945,7 +920,7 @@ void seat_reset_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TOUCH: seat_reset_input_config(seat, seat_device); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: seat_reset_input_config(seat, seat_device); break; case WLR_INPUT_DEVICE_TABLET_PAD: @@ -1026,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) || @@ -1092,20 +1067,10 @@ void seat_configure_xcursor(struct sway_seat *seat) { bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return false; - } - struct wlr_session_lock_surface_v1 *lock_surf; - wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { - if (lock_surf->surface == surface) { - return true; - } - } - return false; + if (server.session_lock.lock) { + return sway_session_lock_has_surface(server.session_lock.lock, surface); } - struct wl_client *client = wl_resource_get_client(surface->resource); - return seat->exclusive_client == client || seat->exclusive_client == NULL; + return true; } static void send_unfocus(struct sway_container *con, void *data) { @@ -1129,6 +1094,7 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { static int handle_urgent_timeout(void *data) { struct sway_view *view = data; view_set_urgent(view, false); + container_update_itself_and_parents(view->container); return 0; } @@ -1310,8 +1276,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } else { seat_set_workspace_focus(seat, node); } - if (server.session_lock.locked) { - seat_set_focus_surface(seat, server.session_lock.focused, false); + if (server.session_lock.lock) { + seat_set_focus_surface(seat, server.session_lock.lock->focused, false); } } @@ -1370,18 +1336,7 @@ void seat_set_focus_layer(struct sway_seat *seat, seat->focused_layer = layer; } -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->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - arrange_layers(output); - } - return; - } +void seat_unfocus_unless_client(struct sway_seat *seat, struct wl_client *client) { if (seat->focused_layer) { if (wl_resource_get_client(seat->focused_layer->resource) != client) { seat_set_focus_layer(seat, NULL); @@ -1408,7 +1363,6 @@ void seat_set_exclusive_client(struct sway_seat *seat, now.tv_nsec / 1000, point->touch_id); } } - seat->exclusive_client = client; } struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, @@ -1575,7 +1529,7 @@ struct seat_config *seat_get_config_by_name(const char *name) { } void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, - uint32_t button, enum wlr_button_state state) { + uint32_t button, enum wl_pointer_button_state state) { seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); } @@ -1612,7 +1566,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { if (seat->seatop_impl->button) { seat->seatop_impl->button(seat, time_msec, device, button, state); } @@ -1745,12 +1699,6 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct fx_render_context *ctx) { - if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, ctx); - } -} - bool seatop_allows_set_cursor(struct sway_seat *seat) { return seat->seatop_impl->allow_set_cursor; } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 1dce6dae..52984818 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -12,10 +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 @@ -55,6 +56,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { while (cont) { if (container_parent_layout(cont) == layout) { list_t *siblings = container_get_siblings(cont); + if (!siblings) { + return false; + } int index = list_find(siblings, cont); if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { return false; @@ -231,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)) && @@ -265,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 && @@ -287,7 +291,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, static bool trigger_pointer_button_binding(struct sway_seat *seat, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state, uint32_t modifiers, + enum wl_pointer_button_state state, uint32_t modifiers, bool on_titlebar, bool on_border, bool on_contents, bool on_workspace) { // We can reach this for non-pointer devices if we're currently emulating // pointer input for one. Emulated input should not trigger bindings. The @@ -301,7 +305,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, char *device_identifier = device ? input_device_get_identifier(device) : strdup("*"); struct sway_binding *binding = NULL; - if (state == WLR_BUTTON_PRESSED) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { state_add_button(e, button); binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, modifiers, false, @@ -326,7 +330,7 @@ static bool trigger_pointer_button_binding(struct sway_seat *seat, static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { struct sway_cursor *cursor = seat->cursor; // Determine what's under the cursor @@ -359,7 +363,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle clicking an empty workspace if (node && node->type == N_WORKSPACE) { - if (state == WLR_BUTTON_PRESSED) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { seat_set_focus(seat, node); transaction_commit_dirty(); } @@ -374,7 +378,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, seat_set_focus_layer(seat, layer); transaction_commit_dirty(); } - if (state == WLR_BUTTON_PRESSED) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { seatop_begin_down_on_surface(seat, surface, sx, sy); } seat_pointer_notify_button(seat, time_msec, button, state); @@ -383,7 +387,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle tiling resize via border if (cont && resize_edge && button == BTN_LEFT && - state == WLR_BUTTON_PRESSED && !is_floating) { + state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating) { // If a resize is triggered on a tabbed or stacked container, change // focus to the tab which already had inactive focus -- otherwise, we'd // change the active tab when the user probably just wanted to resize. @@ -401,7 +405,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle tiling resize via mod bool mod_pressed = modifiers & config->floating_mod; if (cont && !is_floating_or_child && mod_pressed && - state == WLR_BUTTON_PRESSED) { + state == WL_POINTER_BUTTON_STATE_PRESSED) { uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; if (button == btn_resize) { @@ -429,7 +433,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle changing focus when clicking on a container - if (cont && state == WLR_BUTTON_PRESSED) { + if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { // Default case: focus the container that was just clicked. node = &cont->node; @@ -450,7 +454,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle beginning floating move if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { + state == WL_POINTER_BUTTON_STATE_PRESSED) { uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && (mod_pressed || on_titlebar)) { seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); @@ -460,7 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle beginning floating resize if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { + state == WL_POINTER_BUTTON_STATE_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { seat_set_focus_container(seat, cont); @@ -486,8 +490,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle moving a tiling container if (config->tiling_drag && (mod_pressed || on_titlebar) && - state == WLR_BUTTON_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { + state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && + cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE && + button == (config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT)) { + // If moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); @@ -499,19 +505,19 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle mousedown on a container surface - if (surface && cont && state == WLR_BUTTON_PRESSED) { + if (surface && cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { seatop_begin_down(seat, cont, sx, sy); - seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); + seat_pointer_notify_button(seat, time_msec, button, WL_POINTER_BUTTON_STATE_PRESSED); return; } // Handle clicking a container surface or decorations - if (cont && state == WLR_BUTTON_PRESSED) { + if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) { seat_pointer_notify_button(seat, time_msec, button, state); return; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Handle clicking on xwayland unmanaged view struct wlr_xwayland_surface *xsurface; if (surface && @@ -620,12 +626,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } @@ -655,12 +656,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } @@ -691,7 +687,7 @@ static void handle_touch_down(struct sway_seat *seat, pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, dx, dy); dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, - BTN_LEFT, WLR_BUTTON_PRESSED); + BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); } } @@ -701,9 +697,9 @@ static void handle_touch_down(struct sway_seat *seat, static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) { switch (event->orientation) { - case WLR_AXIS_ORIENTATION_VERTICAL: + case WL_POINTER_AXIS_VERTICAL_SCROLL: return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; - case WLR_AXIS_ORIENTATION_HORIZONTAL: + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; default: sway_log(SWAY_DEBUG, "Unknown axis orientation"); @@ -802,8 +798,9 @@ static void handle_pointer_axis(struct sway_seat *seat, if (!handled) { wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - roundf(scroll_factor * event->delta_discrete), event->source); + event->orientation, scroll_factor * event->delta, + roundf(scroll_factor * event->delta_discrete), event->source, + event->relative_direction); } } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 36f9bb60..340e334b 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -118,7 +117,11 @@ static void handle_touch_cancel(struct sway_seat *seat, } if (e->surface) { - wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); + struct wl_client *client = wl_resource_get_client(e->surface->resource); + struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); + if (seat_client != NULL) { + wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); + } } if (wl_list_empty(&e->point_events)) { @@ -137,12 +140,13 @@ static void handle_pointer_axis(struct sway_seat *seat, wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, event->orientation, scroll_factor * event->delta, - roundf(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source, + event->relative_direction); } static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { seat_pointer_notify_button(seat, time_msec, button, state); if (seat->cursor->pressed_button_count == 0) { diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index ddcd4c53..83668d88 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -1,6 +1,4 @@ -#define _POSIX_C_SOURCE 200809L #include -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -23,7 +21,7 @@ static void finalize_move(struct sway_seat *seat) { static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { if (seat->cursor->pressed_button_count == 0) { finalize_move(seat); } @@ -39,9 +37,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_floating_event *e = seat->seatop_data; struct wlr_cursor *cursor = seat->cursor->cursor; - desktop_damage_whole_container(e->con); container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); - desktop_damage_whole_container(e->con); transaction_commit_dirty(); } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index a8cc5f75..1167b18d 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -1,8 +1,7 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include -#include "sway/desktop.h" +#include "scenefx/types/fx/corner_location.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -24,49 +23,17 @@ struct seatop_move_tiling_event { struct sway_container *con; struct sway_node *target_node; enum wlr_edges target_edge; - struct wlr_box drop_box; double ref_lx, ref_ly; // cursor's x/y at start of op bool threshold_reached; bool split_target; bool insert_after_target; + struct wlr_scene_rect *indicator_rect; }; -static void handle_render(struct sway_seat *seat, struct fx_render_context *ctx) { +static void handle_end(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; - if (!e->threshold_reached) { - return; - } - if (e->target_node && node_get_output(e->target_node) == ctx->output) { - float color[4]; - memcpy(&color, config->border_colors.focused.indicator, - sizeof(float) * 4); - premultiply_alpha(color, 0.5); - struct wlr_box box; - memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); - scale_box(&box, ctx->output->wlr_output->scale); - - struct decoration_data deco_data = get_undecorated_decoration_data(); - deco_data.blur = e->con->blur_enabled; - deco_data.corner_radius = e->con->corner_radius * ctx->output->wlr_output->scale; - - // Render blur - if (deco_data.blur && color[3] < 1.0f) { - pixman_region32_t opaque_region; - pixman_region32_init(&opaque_region); - struct wlr_fbox src_box = {0}; - struct wlr_box blur_box; - memcpy(&blur_box, &e->drop_box, sizeof(struct wlr_box)); - // The render_blur function doesn't use root-relative coordinates - blur_box.x -= ctx->output->lx; - blur_box.y -= ctx->output->ly; - scale_box(&blur_box, ctx->output->wlr_output->scale); - - render_blur(ctx, NULL, &src_box, &blur_box, false, &opaque_region, deco_data); - pixman_region32_fini(&opaque_region); - } - - render_rounded_rect(ctx, &box, color, deco_data.corner_radius, ALL); - } + wlr_scene_node_destroy(&e->indicator_rect->node); + e->indicator_rect = NULL; } static void handle_motion_prethreshold(struct sway_seat *seat) { @@ -87,6 +54,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { // If the threshold has been exceeded, start the actual drag if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } @@ -185,6 +153,24 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, return false; } +static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { + wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); + wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); + + int corner_radius = config->corner_radius; + if (e->con) { + corner_radius = e->con->corner_radius; + // The indicator will be shown above a view if the type is of a container, + // otherwise it'll be shown above a container border/title bar + if (e->target_node && e->target_node->type != N_CONTAINER) { + corner_radius += e->con->current.border_thickness; + } + } + wlr_scene_rect_set_corner_radius(e->indicator_rect, + MIN(corner_radius, MIN(box->width / 2, box->height / 2)), + CORNER_LOCATION_ALL); +} + static void handle_motion_postthreshold(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; e->split_target = false; @@ -193,8 +179,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { struct sway_cursor *cursor = seat->cursor; struct sway_node *node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - // Damage the old location - desktop_damage_box(&e->drop_box); if (!node) { // Eg. hovered over a layer surface such as swaybar @@ -207,8 +191,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; - workspace_get_box(node->sway_workspace, &e->drop_box); - desktop_damage_box(&e->drop_box); + + struct wlr_box drop_box; + workspace_get_box(node->sway_workspace, &drop_box); + update_indicator(e, &drop_box); return; } @@ -221,11 +207,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { return; } + struct wlr_box drop_box = { + .x = con->pending.content_x, + .y = con->pending.content_y, + .width = con->pending.content_width, + .height = con->pending.content_height, + }; + // Check if the cursor is over a tilebar only if the destination // container is not a descendant of the source container. if (!surface && !container_has_ancestor(con, e->con) && split_titlebar(node, e->con, cursor->cursor, - &e->drop_box, &e->insert_after_target)) { + &drop_box, &e->insert_after_target)) { // Don't allow dropping over the source container's titlebar // to give users a chance to cancel a drag operation. if (con == e->con) { @@ -235,6 +228,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->split_target = true; } e->target_edge = WLR_EDGE_NONE; + update_indicator(e, &drop_box); return; } @@ -276,8 +270,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->target_node = node_get_parent(e->target_node); } e->target_edge = edge; - e->drop_box = box; - desktop_damage_box(&e->drop_box); + update_indicator(e, &box); return; } con = con->pending.parent; @@ -319,12 +312,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } e->target_node = node; - e->drop_box.x = con->pending.content_x; - e->drop_box.y = con->pending.content_y; - e->drop_box.width = con->pending.content_width; - e->drop_box.height = con->pending.content_height; - resize_box(&e->drop_box, e->target_edge, thickness); - desktop_damage_box(&e->drop_box); + resize_box(&drop_box, e->target_edge, thickness); + update_indicator(e, &drop_box); } static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { @@ -430,7 +419,7 @@ static void finalize_move(struct sway_seat *seat) { static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { if (seat->cursor->pressed_button_count == 0) { finalize_move(seat); } @@ -459,7 +448,7 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_motion = handle_pointer_motion, .tablet_tool_tip = handle_tablet_tool_tip, .unref = handle_unref, - .render = handle_render, + .end = handle_end, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, @@ -471,6 +460,21 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, if (!e) { return; } + + const float *indicator = config->border_colors.focused.indicator; + float color[4] = { + indicator[0] * .5, + indicator[1] * .5, + indicator[2] * .5, + indicator[3] * .5, + }; + e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color); + if (!e->indicator_rect) { + free(e); + return; + } + wlr_scene_rect_set_backdrop_blur(e->indicator_rect, true); + e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index df683026..bec86e33 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -21,7 +20,7 @@ struct seatop_resize_floating_event { static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 869d11b5..15fd333b 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "sway/commands.h" @@ -46,7 +45,7 @@ static struct sway_container *container_get_resize_sibling( static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, - enum wlr_button_state state) { + enum wl_pointer_button_state state) { struct seatop_resize_tiling_event *e = seat->seatop_data; if (seat->cursor->pressed_button_count == 0) { diff --git a/sway/input/switch.c b/sway/input/switch.c index 7a539801..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, @@ -33,9 +34,8 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, } static void execute_binding(struct sway_switch *sway_switch) { - struct sway_seat* seat = sway_switch->seat_device->sway_seat; - bool input_inhibited = seat->exclusive_client != NULL || - server.session_lock.locked; + struct sway_seat *seat = sway_switch->seat_device->sway_seat; + bool locked = server.session_lock.lock; list_t *bindings = config->current_mode->switch_bindings; struct sway_switch_binding *matched_binding = NULL; @@ -52,13 +52,13 @@ static void execute_binding(struct sway_switch *sway_switch) { continue; } bool binding_locked = binding->flags & BINDING_LOCKED; - if (!binding_locked && input_inhibited) { + if (!binding_locked && locked) { continue; } matched_binding = binding; - if (binding_locked == input_inhibited) { + if (binding_locked == locked) { break; } } diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 902cb7ed..ec1e4f68 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -8,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 fec11e77..e1672467 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -1,14 +1,15 @@ #include -#include #include #include "log.h" #include "sway/input/seat.h" -#include "sway/input/text_input.h" +#include "sway/scene_descriptor.h" +#include "sway/tree/root.h" #include "sway/tree/view.h" -#include "sway/tree/container.h" -#include "sway/desktop.h" #include "sway/output.h" +#include "sway/input/text_input.h" +#include "sway/input/text_input_popup.h" #include "sway/layers.h" +#include "sway/server.h" static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { @@ -32,333 +33,6 @@ static struct sway_text_input *relay_get_focused_text_input( return NULL; } -// damage the popup -void sway_input_popup_damage(struct sway_input_popup *popup) { - if (!popup->visible) { - return; - } - - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); - if (text_input == NULL || text_input->input->focused_surface == NULL) { - return; - } - - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_from_wlr_layer_surface_v1(layer_surface); - output_damage_surface(layer->layer_surface->output->data, - layer->geo.x + popup->x, layer->geo.y + popup->y, - popup->popup_surface->surface, true); - return; - } - - struct sway_view *view = view_from_wlr_surface( - text_input->input->focused_surface); - if (view->container == NULL) { - sway_log(SWAY_INFO, "Tried to damage popup, but view is gone"); - return; - } - desktop_damage_surface(popup->popup_surface->surface, - view->container->surface_x - view->geometry.x + popup->x, - view->container->surface_y - view->geometry.y + popup->y, true); -} - -static void input_popup_update(struct sway_input_popup *popup) { - sway_input_popup_damage(popup); - - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); - - if (text_input == NULL || text_input->input->focused_surface == NULL) { - return; - } - - if (!popup->popup_surface->surface->mapped) { - return; - } - - bool cursor_rect = text_input->input->current.features - & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_box cursor = text_input->input->current.cursor_rectangle; - - struct wlr_output *output; - struct wlr_box output_box; - struct wlr_box parent; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_from_wlr_layer_surface_v1(layer_surface); - output = layer->layer_surface->output; - wlr_output_layout_get_box(root->output_layout, output, &output_box); - parent = layer->geo; - parent.x += output_box.x; - parent.y += output_box.y; - } else { - struct sway_view *view = view_from_wlr_surface(focused_surface); - output = wlr_output_layout_output_at(root->output_layout, - view->container->surface_x + view->geometry.x, - view->container->surface_y + view->geometry.y); - wlr_output_layout_get_box(root->output_layout, output, &output_box); - parent.x = view->container->surface_x + view->geometry.x; - parent.y = view->container->surface_y + view->geometry.y; - parent.width = view->geometry.width; - parent.height = view->geometry.height; - } - - if (!cursor_rect) { - cursor.x = 0; - cursor.y = 0; - cursor.width = parent.width; - cursor.height = parent.height; - } - - int popup_width = popup->popup_surface->surface->current.width; - int popup_height = popup->popup_surface->surface->current.height; - int x1 = parent.x + cursor.x; - int x2 = parent.x + cursor.x + cursor.width; - int y1 = parent.y + cursor.y; - int y2 = parent.y + cursor.y + cursor.height; - int x = x1; - int y = y2; - - int available_right = output_box.x + output_box.width - x1; - int available_left = x2 - output_box.x; - if (available_right < popup_width && available_left > available_right) { - x = x2 - popup_width; - } - - int available_down = output_box.y + output_box.height - y2; - int available_up = y1 - output_box.y; - if (available_down < popup_height && available_up > available_down) { - y = y1 - popup_height; - } - - popup->x = x - parent.x; - popup->y = y - parent.y; - - // Hide popup if cursor position is completely out of bounds - bool x1_in_bounds = (cursor.x >= 0 && cursor.x < parent.width); - bool y1_in_bounds = (cursor.y >= 0 && cursor.y < parent.height); - bool x2_in_bounds = (cursor.x + cursor.width >= 0 - && cursor.x + cursor.width < parent.width); - bool y2_in_bounds = (cursor.y + cursor.height >= 0 - && cursor.y + cursor.height < parent.height); - popup->visible = - (x1_in_bounds && y1_in_bounds) || (x2_in_bounds && y2_in_bounds); - - if (cursor_rect) { - struct wlr_box box = { - .x = x1 - x, - .y = y1 - y, - .width = cursor.width, - .height = cursor.height, - }; - wlr_input_popup_surface_v2_send_text_input_rectangle( - popup->popup_surface, &box); - } - - sway_input_popup_damage(popup); -} - -static void surface_send_enter_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - float scale = wlr_output->scale; - wlr_surface_send_enter(surface, wlr_output); - - wlr_fractional_scale_v1_notify_scale(surface, scale); - wlr_surface_set_preferred_buffer_scale(surface, ceil(scale)); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_output); -} - -static void input_popup_send_outputs(struct sway_input_popup *popup, - wlr_surface_iterator_func_t iterator) { - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); - if (text_input == NULL || text_input->input->focused_surface == NULL) { - return; - } - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_from_wlr_layer_surface_v1(layer_surface); - wlr_surface_for_each_surface(popup->popup_surface->surface, - iterator, layer->layer_surface->output); - return; - } - struct sway_view *view = view_from_wlr_surface(focused_surface); - for (int i = 0; i < view->container->outputs->length; i++) { - struct sway_output *output = view->container->outputs->items[i]; - wlr_surface_for_each_surface(popup->popup_surface->surface, - iterator, output->wlr_output); - } -} - -static void handle_im_popup_map(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_map); - input_popup_send_outputs(popup, surface_send_enter_iterator); - input_popup_update(popup); -} - -static void handle_im_popup_unmap(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_unmap); - input_popup_send_outputs(popup, surface_send_leave_iterator); - input_popup_update(popup); -} - -static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_destroy); - wl_list_remove(&popup->focused_surface_unmap.link); - wl_list_remove(&popup->popup_surface_commit.link); - wl_list_remove(&popup->popup_destroy.link); - wl_list_remove(&popup->popup_unmap.link); - wl_list_remove(&popup->popup_map.link); - wl_list_remove(&popup->link); - free(popup); -} - -static void handle_im_popup_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, popup_surface_commit); - input_popup_update(popup); -} - -static void handle_im_focused_surface_unmap( - struct wl_listener *listener, void *data) { - struct sway_input_popup *popup = - wl_container_of(listener, popup, focused_surface_unmap); - input_popup_update(popup); -} - -static void input_popup_set_focus(struct sway_input_popup *popup, - struct wlr_surface *surface) { - wl_list_remove(&popup->focused_surface_unmap.link); - - if (surface == NULL) { - wl_list_init(&popup->focused_surface_unmap.link); - input_popup_update(popup); - return; - } - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(surface); - if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_from_wlr_layer_surface_v1(layer_surface); - wl_signal_add( - &layer->layer_surface->surface->events.unmap, &popup->focused_surface_unmap); - input_popup_update(popup); - return; - } - - struct sway_view *view = view_from_wlr_surface(surface); - wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); - - // Since the focus has changed, the popup may have to adjust -} - -static void handle_im_new_popup_surface(struct wl_listener *listener, - void *data) { - struct sway_input_method_relay *relay = wl_container_of(listener, relay, - input_method_new_popup_surface); - struct sway_input_popup *popup = calloc(1, sizeof(*popup)); - popup->relay = relay; - popup->popup_surface = data; - popup->popup_surface->data = popup; - - wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_map); - popup->popup_map.notify = handle_im_popup_map; - wl_signal_add( - &popup->popup_surface->surface->events.unmap, &popup->popup_unmap); - popup->popup_unmap.notify = handle_im_popup_unmap; - wl_signal_add( - &popup->popup_surface->events.destroy, &popup->popup_destroy); - popup->popup_destroy.notify = handle_im_popup_destroy; - wl_signal_add(&popup->popup_surface->surface->events.commit, - &popup->popup_surface_commit); - popup->popup_surface_commit.notify = handle_im_popup_surface_commit; - wl_list_init(&popup->focused_surface_unmap.link); - popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; - - struct sway_text_input *text_input = relay_get_focused_text_input(relay); - if (text_input != NULL) { - input_popup_set_focus(popup, text_input->input->focused_surface); - } else { - input_popup_set_focus(popup, NULL); - } - - wl_list_insert(&relay->input_popups, &popup->link); -} - -bool sway_input_popup_get_position( - struct sway_input_popup *popup, int *lx, int *ly) { - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); - if (text_input == NULL || text_input->input->focused_surface == NULL) { - *lx = 0; - *ly = 0; - return false; - } - - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_from_wlr_layer_surface_v1(layer_surface); - *lx = layer->geo.x + popup->x; - *ly = layer->geo.y + popup->y; - return true; - } - - - struct sway_view *view = view_from_wlr_surface( - text_input->input->focused_surface); - if (view->container == NULL || view == NULL) { - sway_log(SWAY_INFO, "Tried to find popup, but view is gone"); - return false; - } - *lx = view->container->surface_x - view->geometry.x + popup->x; - *ly = view->container->surface_y - view->geometry.y + popup->y; - return true; -} - -static void text_input_send_enter(struct sway_text_input *text_input, - struct wlr_surface *surface) { - wlr_text_input_v3_send_enter(text_input->input, surface); - struct sway_input_popup *popup; - wl_list_for_each(popup, &text_input->relay->input_popups, link) { - input_popup_set_focus(popup, surface); - } -} - -static void text_input_send_leave(struct sway_text_input *text_input, - struct wlr_surface *surface) { - wlr_text_input_v3_send_leave(text_input->input); - if (text_input->input->focused_surface == surface) { - struct sway_input_popup *popup; - wl_list_for_each(popup, &text_input->relay->input_popups, link) { - input_popup_set_focus(popup, NULL); - } - } -} - static void handle_im_commit(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_commit); @@ -392,11 +66,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); } } @@ -435,6 +111,10 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { input_method_destroy); struct wlr_input_method_v2 *context = data; assert(context == relay->input_method); + wl_list_remove(&relay->input_method_commit.link); + wl_list_remove(&relay->input_method_grab_keyboard.link); + wl_list_remove(&relay->input_method_destroy.link); + wl_list_remove(&relay->input_method_new_popup_surface.link); relay->input_method = NULL; struct sway_text_input *text_input = relay_get_focused_text_input(relay); if (text_input) { @@ -442,10 +122,93 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { // the input method returns text_input_set_pending_focused_surface(text_input, text_input->input->focused_surface); - text_input_send_leave(text_input, text_input->input->focused_surface); + wlr_text_input_v3_send_leave(text_input->input); } } +static void constrain_popup(struct sway_input_popup *popup) { + struct sway_text_input *text_input = + relay_get_focused_text_input(popup->relay); + + if (!popup->desc.relative) { + return; + } + + struct wlr_box parent = {0}; + wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); + + struct wlr_box geo = {0}; + struct wlr_output *output; + + if (popup->desc.view) { + struct sway_view *view = popup->desc.view; + output = wlr_output_layout_output_at(root->output_layout, + view->container->pending.content_x + view->geometry.x, + view->container->pending.content_y + view->geometry.y); + + parent.width = view->geometry.width; + parent.height = view->geometry.height; + geo = view->geometry; + } else { + output = popup->fixed_output; + } + + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output, &output_box); + + bool cursor_rect = text_input->input->current.features & + WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + struct wlr_box cursor_area; + if (cursor_rect) { + cursor_area = text_input->input->current.cursor_rectangle; + } else { + cursor_area = (struct wlr_box) { + .width = parent.width, + .height = parent.height, + }; + } + + int popup_width = popup->popup_surface->surface->current.width; + int popup_height = popup->popup_surface->surface->current.height; + int x1 = parent.x + cursor_area.x; + int x2 = parent.x + cursor_area.x + cursor_area.width; + int y1 = parent.y + cursor_area.y; + int y2 = parent.y + cursor_area.y + cursor_area.height; + int x = x1; + int y = y2; + + int available_right = output_box.x + output_box.width - x1; + int available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + } + + int available_down = output_box.y + output_box.height - y2; + int available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + } + + wlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y); + if (cursor_rect) { + struct wlr_box box = { + .x = x1 - x, + .y = y1 - y, + .width = cursor_area.width, + .height = cursor_area.height, + }; + wlr_input_popup_surface_v2_send_text_input_rectangle( + popup->popup_surface, &box); + } + + if (popup->scene_tree) { + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + } +} + +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface); + static void relay_send_im_state(struct sway_input_method_relay *relay, struct wlr_text_input_v3 *input) { struct wlr_input_method_v2 *input_method = relay->input_method; @@ -466,13 +229,19 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, input->current.content_type.hint, input->current.content_type.purpose); } + + struct sway_text_input *text_input = relay_get_focused_text_input(relay); + struct sway_input_popup *popup; wl_list_for_each(popup, &relay->input_popups, link) { - // send_text_input_rectangle is called in this function - input_popup_update(popup); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } } wlr_input_method_v2_send_done(input_method); - // TODO: pass intent + // TODO: pass intent, display popup size } static void handle_text_input_enable(struct wl_listener *listener, void *data) { @@ -593,6 +362,179 @@ static void relay_handle_text_input(struct wl_listener *listener, sway_text_input_create(relay, wlr_text_input); } +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface) { + wl_list_remove(&popup->focused_surface_unmap.link); + + if (!popup->scene_tree) { + wl_list_init(&popup->focused_surface_unmap.link); + return; + } + + if (popup->desc.relative) { + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); + wlr_scene_node_destroy(popup->desc.relative); + popup->desc.relative = NULL; + } + + if (surface == NULL) { + wl_list_init(&popup->focused_surface_unmap.link); + wlr_scene_node_set_enabled(&popup->scene_tree->node, false); + return; + } + + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(surface); + + struct wlr_scene_tree *relative_parent; + if (layer_surface) { + wl_signal_add(&layer_surface->surface->events.unmap, + &popup->focused_surface_unmap); + + struct sway_layer_surface *layer = layer_surface->data; + if (layer == NULL) { + return; + } + + relative_parent = layer->scene->tree; + popup->desc.view = NULL; + + // we don't need to add an event here to NULL out this field because + // this field will only be initialized if the popup is part of a layer + // surface. Layer surfaces get destroyed as part of the output being + // destroyed, thus also trickling down to popups. + popup->fixed_output = layer->layer_surface->output; + } else { + struct sway_view *view = view_from_wlr_surface(surface); + wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); + relative_parent = view->scene_tree; + popup->desc.view = view; + } + + struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent); + + popup->desc.relative = &relative->node; + if (!scene_descriptor_assign(&popup->scene_tree->node, + SWAY_SCENE_DESC_POPUP, &popup->desc)) { + wlr_scene_node_destroy(&popup->scene_tree->node); + popup->scene_tree = NULL; + return; + } + + constrain_popup(popup); + wlr_scene_node_set_enabled(&popup->scene_tree->node, true); +} + +static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_destroy); + wlr_scene_node_destroy(&popup->scene_tree->node); + wl_list_remove(&popup->focused_surface_unmap.link); + wl_list_remove(&popup->popup_surface_commit.link); + wl_list_remove(&popup->popup_surface_map.link); + wl_list_remove(&popup->popup_surface_unmap.link); + wl_list_remove(&popup->popup_destroy.link); + wl_list_remove(&popup->link); + + free(popup); +} + +static void handle_im_popup_surface_map(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_map); + struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } +} + +static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_unmap); + + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); + // relative should already be freed as it should be a child of the just unmapped scene + popup->desc.relative = NULL; + + input_popup_set_focus(popup, NULL); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_commit); + + constrain_popup(popup); +} + +static void handle_im_focused_surface_unmap( + struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, focused_surface_unmap); + + input_popup_set_focus(popup, NULL); +} + +static void handle_im_new_popup_surface(struct wl_listener *listener, + void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new_popup_surface); + struct sway_input_popup *popup = calloc(1, sizeof(*popup)); + if (!popup) { + sway_log(SWAY_ERROR, "Failed to allocate an input method popup"); + return; + } + + popup->relay = relay; + popup->popup_surface = data; + popup->popup_surface->data = popup; + + popup->scene_tree = wlr_scene_tree_create(root->layers.popup); + if (!popup->scene_tree) { + sway_log(SWAY_ERROR, "Failed to allocate scene tree"); + free(popup); + return; + } + + if (!wlr_scene_subsurface_tree_create(popup->scene_tree, + popup->popup_surface->surface)) { + sway_log(SWAY_ERROR, "Failed to allocate subsurface tree"); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return; + } + + wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->popup_destroy.notify = handle_im_popup_destroy; + wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit); + popup->popup_surface_commit.notify = handle_im_popup_surface_commit; + wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map); + popup->popup_surface_map.notify = handle_im_popup_surface_map; + wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap); + popup->popup_surface_unmap.notify = handle_im_popup_surface_unmap; + wl_list_init(&popup->focused_surface_unmap.link); + popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; + + struct sway_text_input *text_input = relay_get_focused_text_input(relay); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } + + wl_list_insert(&relay->input_popups, &popup->link); +} + +static void text_input_send_enter(struct sway_text_input *text_input, + struct wlr_surface *surface) { + wlr_text_input_v3_send_enter(text_input->input, surface); + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + input_popup_set_focus(popup, surface); + } +} + static void relay_handle_input_method(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, @@ -612,19 +554,20 @@ static void relay_handle_input_method(struct wl_listener *listener, wl_signal_add(&relay->input_method->events.commit, &relay->input_method_commit); relay->input_method_commit.notify = handle_im_commit; - wl_signal_add(&relay->input_method->events.new_popup_surface, - &relay->input_method_new_popup_surface); - relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; wl_signal_add(&relay->input_method->events.grab_keyboard, &relay->input_method_grab_keyboard); relay->input_method_grab_keyboard.notify = handle_im_grab_keyboard; wl_signal_add(&relay->input_method->events.destroy, &relay->input_method_destroy); relay->input_method_destroy.notify = handle_im_destroy; + wl_signal_add(&relay->input_method->events.new_popup_surface, + &relay->input_method_new_popup_surface); + relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; struct sway_text_input *text_input = relay_get_focusable_text_input(relay); if (text_input) { - text_input_send_enter(text_input, text_input->pending_focused_surface); + text_input_send_enter(text_input, + text_input->pending_focused_surface); text_input_set_pending_focused_surface(text_input, NULL); } } @@ -662,9 +605,8 @@ void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, } else if (text_input->input->focused_surface) { assert(text_input->pending_focused_surface == NULL); if (surface != text_input->input->focused_surface) { - text_input_send_leave( - text_input, text_input->input->focused_surface); relay_disable_text_input(relay, text_input); + wlr_text_input_v3_send_leave(text_input->input); } else { sway_log(SWAY_DEBUG, "IM relay set_focus already focused"); continue; @@ -675,7 +617,7 @@ void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, && wl_resource_get_client(text_input->input->resource) == wl_resource_get_client(surface->resource)) { if (relay->input_method) { - text_input_send_enter(text_input, surface); + wlr_text_input_v3_send_enter(text_input->input, surface); } else { text_input_set_pending_focused_surface(text_input, surface); } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index d5866ef6..5be7090c 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -8,11 +8,15 @@ #include #include #include "config.h" +#include "json_object.h" #include "log.h" #include "sway/config.h" #include "sway/ipc-json.h" #include "sway/layers.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" #include "sway/tree/workspace.h" #include "sway/output.h" @@ -155,7 +159,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; @@ -290,12 +294,13 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name, json_object_object_add(object, "focus", focus); json_object_object_add(object, "fullscreen_mode", json_object_new_int(0)); json_object_object_add(object, "sticky", json_object_new_boolean(false)); + json_object_object_add(object, "floating", NULL); + json_object_object_add(object, "scratchpad_state", NULL); return object; } -static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, - struct sway_output *output, json_object *object) { +static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "make", json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); @@ -321,7 +326,7 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, static void ipc_json_describe_output(struct sway_output *output, json_object *object) { - ipc_json_describe_wlr_output(output->wlr_output, output, object); + ipc_json_describe_wlr_output(output->wlr_output, object); } static void ipc_json_describe_enabled_output(struct sway_output *output, @@ -353,60 +358,76 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, json_object_object_add(object, "adaptive_sync_status", json_object_new_string(adaptive_sync_status)); + // Layer effects struct json_object *layers = json_object_new_array(); - size_t len = sizeof(output->layers) / sizeof(output->layers[0]); - for (size_t i = 0; i < len; ++i) { - struct sway_layer_surface *lsurface; - wl_list_for_each(lsurface, &output->layers[i], link) { - json_object *layer = json_object_new_object(); + struct wlr_scene_tree *scene_layers[] = { + output->layers.shell_background, + output->layers.shell_bottom, + output->layers.shell_overlay, + output->layers.shell_top, + }; + size_t nlayers = sizeof(scene_layers) / sizeof(scene_layers[0]); + struct wlr_scene_node *node; + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(node, &scene_layers[i]->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (!surface || !surface->layer_surface) { + continue; + } + struct wlr_layer_surface_v1 *lsurface = surface->layer_surface; - json_object_object_add(layer, "namespace", - json_object_new_string(lsurface->layer_surface->namespace)); - - char *layer_name = NULL; - switch (lsurface->layer) { - case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: - layer_name = "background"; - break; - case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: - layer_name = "bottom"; - break; - case ZWLR_LAYER_SHELL_V1_LAYER_TOP: - layer_name = "top"; - break; - case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: - layer_name = "overlay"; - break; + // Get the node coordinates + int layer_x, layer_y; + if (wlr_scene_node_coords(&surface->tree->node, &layer_x, &layer_y)) { + // Transform the root-local coordinates to output-local + double lx = layer_x, ly = layer_y; + wlr_output_layout_output_coords(root->output_layout, + output->wlr_output, &lx, &ly); + layer_x = lx; + layer_y = ly; + } else { + // The surface is not visible + continue; } - json_object_object_add(layer, "layer", - json_object_new_string(layer_name)); + json_object *layer = json_object_new_object(); + json_object_object_add(layer, "namespace", + json_object_new_string(surface->layer_surface->namespace)); + + struct json_object *layer_name = NULL; + switch (lsurface->current.layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + layer_name = json_object_new_string("background"); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + layer_name = json_object_new_string("bottom"); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + layer_name = json_object_new_string("top"); + break; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + layer_name = json_object_new_string("overlay"); + break; + } + json_object_object_add(layer, "layer", layer_name); json_object *extent = json_object_new_object(); json_object_object_add(extent, "width", - json_object_new_int(lsurface->extent.width)); + json_object_new_int(lsurface->current.actual_width)); json_object_object_add(extent, "height", - json_object_new_int(lsurface->extent.height)); - json_object_object_add(extent, "x", - json_object_new_int(lsurface->extent.x)); - json_object_object_add(extent, "y", - json_object_new_int(lsurface->extent.y)); + json_object_new_int(lsurface->current.actual_height)); + json_object_object_add(extent, "x", json_object_new_int(layer_x)); + json_object_object_add(extent, "y", json_object_new_int(layer_y)); json_object_object_add(layer, "extent", extent); - json_object *effects = json_object_new_array(); - if (lsurface->has_blur) { - json_object_array_add(effects, json_object_new_string("blur")); - } - if (lsurface->blur_ignore_transparent) { - json_object_array_add(effects, - json_object_new_string("blur_ignore_transparent")); - } - if (lsurface->has_shadow) { - json_object_array_add(effects, json_object_new_string("shadows")); - } - if (lsurface->corner_radius > 0) { - json_object_array_add(effects, json_object_new_string("corner_radius")); - } + json_object *effects = json_object_new_object(); + json_object_object_add(effects, "blur", json_object_new_boolean(surface->blur_enabled)); + json_object_object_add(effects, "blur_xray", json_object_new_boolean(surface->blur_xray)); + json_object_object_add(effects, "blur_ignore_transparent", + json_object_new_boolean(surface->blur_ignore_transparent)); + json_object_object_add(effects, "shadows", json_object_new_boolean(surface->shadow_enabled)); + json_object_object_add(effects, "corner_radius", json_object_new_int(surface->corner_radius)); json_object_object_add(layer, "effects", effects); json_object_array_add(layers, layer); @@ -460,6 +481,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, } json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); + + json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing)); } json_object *ipc_json_describe_disabled_output(struct sway_output *output) { @@ -496,7 +519,7 @@ json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop json_object *object = json_object_new_object(); - ipc_json_describe_wlr_output(wlr_output, NULL, object); + ipc_json_describe_wlr_output(wlr_output, object); json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); json_object_object_add(object, "type", json_object_new_string("output")); @@ -639,9 +662,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 }; @@ -653,6 +677,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time)); + json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view))); + json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view))); json_object_object_add(object, "inhibit_idle", @@ -695,7 +721,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))); @@ -739,7 +765,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object static void ipc_json_describe_container(struct sway_container *c, json_object *object) { json_object_object_add(object, "name", c->title ? json_object_new_string(c->title) : NULL); - if (container_is_floating(c)) { + bool floating = container_is_floating(c); + if (floating) { json_object_object_add(object, "type", json_object_new_string("floating_con")); } @@ -757,9 +784,17 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); json_object_object_add(object, "sticky", json_object_new_boolean(c->is_sticky)); + // sway doesn't track the floating reason, so we can't use "auto_on" or "user_off" + json_object_object_add(object, "floating", + json_object_new_string(floating ? "user_on" : "auto_off")); + json_object_object_add(object, "fullscreen_mode", json_object_new_int(c->pending.fullscreen_mode)); + // sway doesn't track if window was resized in scratchpad, so we can't use "changed" + json_object_object_add(object, "scratchpad_state", + json_object_new_string(!c->scratchpad ? "none" : "fresh")); + struct sway_node *parent = node_get_parent(&c->node); struct wlr_box parent_box = {0, 0, 0, 0}; @@ -978,6 +1013,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: drag_lock = "disabled"; break; +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY: + drag_lock = "enabled_sticky"; + break; +#endif } json_object_object_add(object, "tap_drag_lock", json_object_new_string(drag_lock)); @@ -1043,6 +1083,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)) { @@ -1150,10 +1202,6 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_string(device->identifier)); json_object_object_add(object, "name", json_object_new_string(device->wlr_device->name)); - json_object_object_add(object, "vendor", - json_object_new_int(device->wlr_device->vendor)); - json_object_object_add(object, "product", - json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( input_device_get_type(device))); @@ -1172,7 +1220,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object *layouts_arr = json_object_new_array(); json_object_object_add(object, "xkb_layout_names", layouts_arr); - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); + xkb_layout_index_t num_layouts = + keymap ? xkb_keymap_num_layouts(keymap) : 0; + // Virtual keyboards might have null keymap xkb_layout_index_t layout_idx; for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); @@ -1207,6 +1257,10 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); json_object_object_add(object, "libinput", describe_libinput_device(libinput_dev)); + json_object_object_add(object, "vendor", + json_object_new_int(libinput_device_get_id_vendor(libinput_dev))); + json_object_object_add(object, "product", + json_object_new_int(libinput_device_get_id_product(libinput_dev))); } #endif diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 9692a77f..b934bb56 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -1,5 +1,4 @@ // See https://i3wm.org/docs/ipc.html for protocol information -#define _POSIX_C_SOURCE 200112L #include #include #include @@ -649,6 +648,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } list_t *res_list = execute_command(buf, NULL, NULL); + if (modeset_is_pending()) { + // IPC expects commands to have taken immediate effect, so we need + // to force a modeset after output commands. We do a single modeset + // here to avoid modesetting for every output command in sequence. + force_modeset(); + } transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); diff --git a/sway/layer_criteria.c b/sway/layer_criteria.c index f7a1b084..56521693 100644 --- a/sway/layer_criteria.c +++ b/sway/layer_criteria.c @@ -1,45 +1,46 @@ #include +#include +#include "list.h" #include "log.h" #include "stringop.h" #include "sway/commands.h" #include "sway/layer_criteria.h" #include "util.h" -void layer_criteria_destroy(struct layer_criteria *criteria) { - free(criteria->namespace); - free(criteria->cmdlist); - free(criteria); +static void init_criteria_effects(struct layer_criteria *criteria) { + criteria->corner_radius = 0; + criteria->blur_enabled = false; + criteria->blur_xray = false; + criteria->blur_ignore_transparent = false; + criteria->shadow_enabled = false; } -bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b) { - return strcmp(a->namespace, b->namespace) == 0 - && strcmp(a->cmdlist, b->cmdlist) == 0; +static void copy_criteria_effects(struct layer_criteria *dst, struct layer_criteria *src) { + dst->corner_radius = src->corner_radius; + dst->blur_enabled = src->blur_enabled; + dst->blur_xray = src->blur_xray; + dst->blur_ignore_transparent = src->blur_ignore_transparent; + dst->shadow_enabled = src->shadow_enabled; } -bool layer_criteria_already_exists(struct layer_criteria *criteria) { +static bool layer_criteria_find(char *namespace, + struct layer_criteria **criteria, int *index) { + *index = -1; + *criteria = NULL; + list_t *criterias = config->layer_criteria; for (int i = 0; i < criterias->length; ++i) { struct layer_criteria *existing = criterias->items[i]; - if (layer_criteria_is_equal(criteria, existing)) { + if (strcmp(namespace, existing->namespace) == 0) { + *index = i; + *criteria = existing; return true; } } return false; } -list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer) { - list_t *criterias = config->layer_criteria; - list_t *matches = create_list(); - for (int i = 0; i < criterias->length; ++i) { - struct layer_criteria *criteria = criterias->items[i]; - if (strcmp(criteria->namespace, sway_layer->layer_surface->namespace) == 0) { - list_add(matches, criteria); - } - } - return matches; -} - -void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria) { +static bool layer_criteria_parse(struct layer_criteria *criteria) { char matched_delim = ';'; char *head = malloc(strlen(criteria->cmdlist) + 1); strcpy(head, criteria->cmdlist); @@ -64,28 +65,91 @@ void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_cr strip_quotes(argv[i]); } } - if (strcmp(argv[0], "blur") == 0) { - sway_layer->has_blur = parse_boolean(argv[1], true); + if (strcmp(argv[0], "reset") == 0) { + // Reset all args + init_criteria_effects(criteria); + return true; + } else if (strcmp(argv[0], "blur") == 0) { + criteria->blur_enabled = parse_boolean(argv[1], true); + continue; + } if (strcmp(argv[0], "blur_xray") == 0) { + criteria->blur_xray = parse_boolean(argv[1], true); continue; } if (strcmp(argv[0], "blur_ignore_transparent") == 0) { - sway_layer->blur_ignore_transparent = parse_boolean(argv[1], true); + criteria->blur_ignore_transparent = parse_boolean(argv[1], true); continue; } else if (strcmp(argv[0], "shadows") == 0) { - sway_layer->has_shadow = parse_boolean(argv[1], true); + criteria->shadow_enabled = parse_boolean(argv[1], true); continue; } else if (strcmp(argv[0], "corner_radius") == 0) { int value; if (cmd_corner_radius_parse_value(argv[1], &value)) { - sway_layer->corner_radius = value; + criteria->corner_radius = value; continue; } sway_log(SWAY_ERROR, "Invalid layer_effects corner_radius size! Got \"%s\"", argv[1]); - return; + return false; } else { sway_log(SWAY_ERROR, "Invalid layer_effects effect! Got \"%s\"", cmd); - return; + return false; } } while(head); + + return true; +} + +struct layer_criteria *layer_criteria_add(char *namespace, char *cmdlist) { + struct layer_criteria *criteria = calloc(1, sizeof(*criteria)); + init_criteria_effects(criteria); + + // Check if the rule already exists + int existing_index; + struct layer_criteria *existing = NULL; + if (layer_criteria_find(namespace, &existing, &existing_index)) { + sway_log(SWAY_DEBUG, "layer_effect '%s' already exists: '%s'. Replacing with: '%s'", + namespace, existing->cmdlist, cmdlist); + // Clone the effects into the new criteria + copy_criteria_effects(criteria, existing); + layer_criteria_destroy(existing); + list_del(config->layer_criteria, existing_index); + } + + // Create a new criteria + if (!criteria) { + sway_log(SWAY_ERROR, "Could not allocate new layer_criteria"); + return NULL; + } + criteria->namespace = malloc(strlen(namespace) + 1); + strcpy(criteria->namespace, namespace); + criteria->cmdlist = cmdlist; + + // Parsing + if (!layer_criteria_parse(criteria)) { + // Failed to parse + layer_criteria_destroy(criteria); + return NULL; + } + + list_add(config->layer_criteria, criteria); + + return criteria; +} + +struct layer_criteria *layer_criteria_for_namespace(char *namespace) { + list_t *criterias = config->layer_criteria; + for (int i = criterias->length - 1; i >= 0; i--) { + struct layer_criteria *criteria = criterias->items[i]; + if (strcmp(criteria->namespace, namespace) == 0) { + return criteria; + } + } + return NULL; +} + +void layer_criteria_destroy(struct layer_criteria *criteria) { + free(criteria->namespace); + free(criteria->cmdlist); + free(criteria); } diff --git a/sway/lock.c b/sway/lock.c index 199624fc..bfb21497 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,26 +1,36 @@ -#define _POSIX_C_SOURCE 200809L #include +#include +#include #include "log.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" +#include "sway/lock.h" + +struct sway_session_lock_output { + struct wlr_scene_tree *tree; + struct wlr_scene_rect *background; + struct sway_session_lock *lock; -struct sway_session_lock_surface { - struct wlr_session_lock_surface_v1 *lock_surface; struct sway_output *output; - struct wlr_surface *surface; - struct wl_listener map; + + struct wl_list link; // sway_session_lock::outputs + struct wl_listener destroy; - struct wl_listener surface_commit; - struct wl_listener output_commit; - struct wl_listener output_destroy; + + struct wlr_session_lock_surface_v1 *surface; + + // invalid if surface is NULL + struct wl_listener surface_destroy; + struct wl_listener surface_map; }; -static void set_lock_focused_surface(struct wlr_surface *focused) { - server.session_lock.focused = focused; +static void focus_surface(struct sway_session_lock *lock, + struct wlr_surface *focused) { + lock->focused = focused; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { @@ -28,108 +38,187 @@ static void set_lock_focused_surface(struct wlr_surface *focused) { } } -static void handle_surface_map(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); - if (server.session_lock.focused == NULL) { - set_lock_focused_surface(surf->surface); - } - cursor_rebase_all(); - surface_enter_output(surf->surface, surf->output); - output_damage_whole(surf->output); -} - -static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); - output_damage_surface(surf->output, 0, 0, surf->surface, false); -} - -static void handle_output_commit(struct wl_listener *listener, void *data) { - struct wlr_output_event_commit *event = data; - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_TRANSFORM)) { - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); - } -} - -static void destroy_lock_surface(struct sway_session_lock_surface *surf) { +static void refocus_output(struct sway_session_lock_output *output) { // Move the seat focus to another surface if one is available - if (server.session_lock.focused == surf->surface) { + if (output->lock->focused == output->surface->surface) { struct wlr_surface *next_focus = NULL; - struct wlr_session_lock_surface_v1 *other; - wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { - if (other != surf->lock_surface && other->surface->mapped) { - next_focus = other->surface; + struct sway_session_lock_output *candidate; + wl_list_for_each(candidate, &output->lock->outputs, link) { + if (candidate == output || !candidate->surface) { + continue; + } + + if (candidate->surface->surface->mapped) { + next_focus = candidate->surface->surface; break; } } - set_lock_focused_surface(next_focus); - } - wl_list_remove(&surf->map.link); - wl_list_remove(&surf->destroy.link); - wl_list_remove(&surf->surface_commit.link); - wl_list_remove(&surf->output_commit.link); - wl_list_remove(&surf->output_destroy.link); - output_damage_whole(surf->output); - free(surf); + focus_surface(output->lock, next_focus); + } +} + +static void handle_surface_map(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); + if (surf->lock->focused == NULL) { + focus_surface(surf->lock, surf->surface->surface); + } + cursor_rebase_all(); } static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); - destroy_lock_surface(surf); + struct sway_session_lock_output *output = + wl_container_of(listener, output, surface_destroy); + refocus_output(output); + + sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); + output->surface = NULL; + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); } -static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = - wl_container_of(listener, surf, output_destroy); - destroy_lock_surface(surf); +static void lock_output_reconfigure(struct sway_session_lock_output *output) { + int width = output->output->width; + int height = output->output->height; + + wlr_scene_rect_set_size(output->background, width, height); + + if (output->surface) { + wlr_session_lock_surface_v1_configure(output->surface, width, height); + } } -static void handle_new_surface(struct wl_listener *listener, void *data) { - struct wlr_session_lock_surface_v1 *lock_surface = data; - struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); - if (surf == NULL) { +void arrange_locks(void) { + if (server.session_lock.lock == NULL) { return; } + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) { + lock_output_reconfigure(lock_output); + } +} + +static void handle_new_surface(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); + struct wlr_session_lock_surface_v1 *lock_surface = data; + struct sway_output *output = lock_surface->output->data; + sway_log(SWAY_DEBUG, "new lock layer surface"); - struct sway_output *output = lock_surface->output->data; - wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); + struct sway_session_lock_output *current_lock_output, *lock_output = NULL; + wl_list_for_each(current_lock_output, &lock->outputs, link) { + if (current_lock_output->output == output) { + lock_output = current_lock_output; + break; + } + } + sway_assert(lock_output, "Couldn't find output to lock"); + sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); - surf->lock_surface = lock_surface; - surf->surface = lock_surface->surface; - surf->output = output; - surf->map.notify = handle_surface_map; - wl_signal_add(&lock_surface->surface->events.map, &surf->map); - surf->destroy.notify = handle_surface_destroy; - wl_signal_add(&lock_surface->events.destroy, &surf->destroy); - surf->surface_commit.notify = handle_surface_commit; - wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); - surf->output_commit.notify = handle_output_commit; - wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); - surf->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->node.events.destroy, &surf->output_destroy); + lock_output->surface = lock_surface; + + wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); + + lock_output->surface_destroy.notify = handle_surface_destroy; + wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); + lock_output->surface_map.notify = handle_surface_map; + wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); + + lock_output_reconfigure(lock_output); +} + +static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { + if (output->surface) { + refocus_output(output); + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); + } + + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + + free(output); +} + +static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, destroy); + sway_session_lock_output_destroy(output); +} + +static struct sway_session_lock_output *session_lock_output_create( + struct sway_session_lock *lock, struct sway_output *output) { + struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); + if (!lock_output) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output"); + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); + if (!tree) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); + free(lock_output); + return NULL; + } + + struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ + lock->abandoned ? 1.f : 0.f, + 0.f, + 0.f, + 1.f, + }); + if (!background) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); + wlr_scene_node_destroy(&tree->node); + free(lock_output); + return NULL; + } + + lock_output->output = output; + lock_output->tree = tree; + lock_output->background = background; + lock_output->lock = lock; + + lock_output->destroy.notify = lock_node_handle_destroy; + wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); + + lock_output_reconfigure(lock_output); + + wl_list_insert(&lock->outputs, &lock_output->link); + + return lock_output; +} + +static void sway_session_lock_destroy(struct sway_session_lock* lock) { + struct sway_session_lock_output *lock_output, *tmp_lock_output; + wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { + // destroying the node will also destroy the whole lock output + wlr_scene_node_destroy(&lock_output->tree->node); + } + + if (server.session_lock.lock == lock) { + server.session_lock.lock = NULL; + } + + if (!lock->abandoned) { + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); + } + + free(lock); } static void handle_unlock(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); sway_log(SWAY_DEBUG, "session unlocked"); - server.session_lock.locked = false; - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); + sway_session_lock_destroy(lock); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - seat_set_exclusive_client(seat, NULL); // copied from seat_set_focus_layer -- deduplicate? struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); if (previous) { @@ -139,32 +228,28 @@ static void handle_unlock(struct wl_listener *listener, void *data) { } } - // redraw everything + // 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->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); + arrange_layers(output); } } static void handle_abandon(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); sway_log(SWAY_INFO, "session lock abandoned"); - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat->exclusive_client = NULL; + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &lock->outputs, link) { + wlr_scene_rect_set_color(lock_output->background, + (float[4]){ 1.f, 0.f, 0.f, 1.f }); } - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + lock->abandoned = true; + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); } static void handle_session_lock(struct wl_listener *listener, void *data) { @@ -172,44 +257,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { struct wl_client *client = wl_resource_get_client(lock->resource); if (server.session_lock.lock) { + if (server.session_lock.lock->abandoned) { + sway_log(SWAY_INFO, "Replacing abandoned lock"); + sway_session_lock_destroy(server.session_lock.lock); + } else { + sway_log(SWAY_ERROR, "Cannot lock an already locked session"); + wlr_session_lock_v1_destroy(lock); + return; + } + } + + struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); + if (!sway_lock) { + sway_log(SWAY_ERROR, "failed to allocate a session lock object"); wlr_session_lock_v1_destroy(lock); return; } + wl_list_init(&sway_lock->outputs); + sway_log(SWAY_DEBUG, "session locked"); - server.session_lock.locked = true; - server.session_lock.lock = lock; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - seat_set_exclusive_client(seat, client); + seat_unfocus_unless_client(seat, client); } - wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); - wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); - wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + sway_session_lock_add_output(sway_lock, output); + } + + sway_lock->new_surface.notify = handle_new_surface; + wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); + sway_lock->unlock.notify = handle_unlock; + wl_signal_add(&lock->events.unlock, &sway_lock->unlock); + sway_lock->destroy.notify = handle_abandon; + wl_signal_add(&lock->events.destroy, &sway_lock->destroy); wlr_session_lock_v1_send_locked(lock); - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + server.session_lock.lock = sway_lock; } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { - assert(server.session_lock.lock == NULL); + // if the server shuts down while a lock is active, destroy the lock + if (server.session_lock.lock) { + sway_session_lock_destroy(server.session_lock.lock); + } + wl_list_remove(&server.session_lock.new_lock.link); wl_list_remove(&server.session_lock.manager_destroy.link); + + server.session_lock.manager = NULL; +} + +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output) { + struct sway_session_lock_output *lock_output = + session_lock_output_create(lock, output); + + // if we run out of memory while trying to lock the screen, the best we + // can do is kill the sway process. Security conscious users will have + // the sway session fall back to a login shell. + if (!lock_output) { + sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); + abort(); + } +} + +bool sway_session_lock_has_surface(struct sway_session_lock *lock, + struct wlr_surface *surface) { + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &lock->outputs, link) { + if (lock_output->surface && lock_output->surface->surface == surface) { + return true; + } + } + + return false; } void sway_session_lock_init(void) { server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); - server.session_lock.lock_new_surface.notify = handle_new_surface; - server.session_lock.lock_unlock.notify = handle_unlock; - server.session_lock.lock_destroy.notify = handle_abandon; server.session_lock.new_lock.notify = handle_session_lock; server.session_lock.manager_destroy.notify = handle_session_lock_destroy; wl_signal_add(&server.session_lock.manager->events.new_lock, diff --git a/sway/main.c b/sway/main.c index 681a24e9..7e41de60 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -49,44 +48,6 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } -void detect_proprietary(int allow_unsupported_gpu) { - FILE *f = fopen("/proc/modules", "r"); - if (!f) { - return; - } - char *line = NULL; - size_t line_size = 0; - while (getline(&line, &line_size, f) != -1) { - if (strncmp(line, "nvidia ", 7) == 0) { - if (allow_unsupported_gpu) { - sway_log(SWAY_ERROR, - "!!! Proprietary Nvidia drivers are in use !!!"); - } else { - sway_log(SWAY_ERROR, - "Proprietary Nvidia drivers are NOT supported. " - "Use Nouveau. To launch sway anyway, launch with " - "--unsupported-gpu and DO NOT report issues."); - exit(EXIT_FAILURE); - } - break; - } - if (strstr(line, "fglrx")) { - if (allow_unsupported_gpu) { - sway_log(SWAY_ERROR, - "!!! Proprietary AMD drivers are in use !!!"); - } else { - sway_log(SWAY_ERROR, "Proprietary AMD drivers do NOT support " - "Wayland. Use radeon. To try anyway, launch sway with " - "--unsupported-gpu and DO NOT report issues."); - exit(EXIT_FAILURE); - } - break; - } - } - free(line); - fclose(f); -} - void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -192,11 +153,7 @@ void restore_nofile_limit(void) { } void enable_debug_flag(const char *flag) { - if (strcmp(flag, "damage=highlight") == 0) { - debug.damage = DAMAGE_HIGHLIGHT; - } else if (strcmp(flag, "damage=rerender") == 0) { - debug.damage = DAMAGE_RERENDER; - } else if (strcmp(flag, "noatomic") == 0) { + if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; @@ -204,8 +161,8 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (strncmp(flag, "txn-timeout=", 12) == 0) { server.txn_timeout_ms = atoi(&flag[12]); - } else if (strcmp(flag, "noscanout") == 0) { - debug.noscanout = true; + } else if (strcmp(flag, "legacy-wl-drm") == 0) { + debug.legacy_wl_drm = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } @@ -255,7 +212,7 @@ static const char usage[] = "\n"; int main(int argc, char **argv) { - static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; + static bool verbose = false, debug = false, validate = false; char *config_path = NULL; @@ -337,6 +294,7 @@ int main(int argc, char **argv) { sway_log(SWAY_INFO, "swayfx version " SWAY_VERSION " (based on sway version " SWAY_ORIGINAL_VERSION ")"); sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); + sway_log(SWAY_INFO, "scenefx version " SCENEFX_VERSION); log_kernel(); log_distro(); log_env(); @@ -363,7 +321,6 @@ int main(int argc, char **argv) { return 0; } - detect_proprietary(allow_unsupported_gpu); increase_nofile_limit(); // handle SIGTERM signals @@ -376,12 +333,14 @@ int main(int argc, char **argv) { sway_log(SWAY_INFO, "Starting swayfx version " SWAY_VERSION " (based on sway version " SWAY_ORIGINAL_VERSION ")"); - root = root_create(); - if (!server_init(&server)) { return 1; } + if (server.linux_dmabuf_v1) { + wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); + } + if (validate) { bool valid = load_main_config(config_path, false, true); free(config_path); @@ -404,6 +363,7 @@ int main(int argc, char **argv) { } config->active = true; + force_modeset(); load_swaybars(); run_deferred_commands(); run_deferred_bindings(); diff --git a/sway/meson.build b/sway/meson.build index 40c7382e..25f97d78 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -9,17 +9,17 @@ sway_sources = files( 'lock.c', 'main.c', 'realtime.c', + 'scene_descriptor.c', 'server.c', + 'sway_text_node.c', 'swaynag.c', 'xdg_activation_v1.c', 'xdg_decoration.c', - 'desktop/desktop.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', - 'desktop/render.c', - 'desktop/surface.c', + 'desktop/tearing.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', 'desktop/launcher.c', @@ -43,6 +43,7 @@ sway_sources = files( 'config/seat.c', 'config/input.c', + 'commands/allow_tearing.c', 'commands/assign.c', 'commands/bar.c', 'commands/bind.c', @@ -57,7 +58,6 @@ sway_sources = files( 'commands/border.c', 'commands/client.c', 'commands/corner_radius.c', - 'commands/smart_corner_radius.c', 'commands/create_output.c', 'commands/default_border.c', 'commands/default_dim_inactive.c', @@ -105,7 +105,6 @@ sway_sources = files( 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', - 'commands/saturation.c', 'commands/scratchpad.c', 'commands/scratchpad_minimize.c', 'commands/seat.c', @@ -118,17 +117,17 @@ sway_sources = files( 'commands/seat/pointer_constraint.c', 'commands/seat/shortcuts_inhibitor.c', 'commands/seat/xcursor_theme.c', - 'commands/titlebar_separator.c', 'commands/set.c', 'commands/shadow_blur_radius.c', 'commands/shadow_color.c', - 'commands/shadow_offset.c', 'commands/shadow_inactive_color.c', + 'commands/shadow_offset.c', 'commands/shadows.c', 'commands/shadows_on_csd.c', 'commands/show_marks.c', 'commands/shortcuts_inhibitor.c', 'commands/smart_borders.c', + 'commands/smart_corner_radius.c', 'commands/smart_gaps.c', 'commands/split.c', 'commands/sticky.c', @@ -141,6 +140,7 @@ sway_sources = files( 'commands/title_format.c', 'commands/titlebar_border_thickness.c', 'commands/titlebar_padding.c', + 'commands/titlebar_separator.c', 'commands/unmark.c', 'commands/urgent.c', 'commands/workspace.c', @@ -179,6 +179,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', @@ -191,7 +192,7 @@ sway_sources = files( 'commands/input/middle_emulation.c', 'commands/input/natural_scroll.c', 'commands/input/pointer_accel.c', - 'commands/input/rotation_angle.c', + 'commands/input/rotation_angle.c', 'commands/input/repeat_delay.c', 'commands/input/repeat_rate.c', 'commands/input/scroll_button.c', @@ -212,6 +213,7 @@ sway_sources = files( 'commands/input/xkb_variant.c', 'commands/output/adaptive_sync.c', + 'commands/output/allow_tearing.c', 'commands/output/background.c', 'commands/output/disable.c', 'commands/output/dpms.c', @@ -227,6 +229,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', @@ -255,10 +258,9 @@ sway_deps = [ xkbcommon, xcb, xcb_icccm, - egl, ] -if have_xwayland +if wlroots_features['xwayland'] sway_sources += 'desktop/xwayland.c' endif @@ -274,4 +276,3 @@ executable( link_with: [lib_sway_common], install: true ) - diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 00000000..92bdda00 --- /dev/null +++ b/sway/scene_descriptor.c @@ -0,0 +1,69 @@ +#include +#include +#include "log.h" +#include "sway/scene_descriptor.h" + +struct scene_descriptor { + void *data; + struct wlr_addon addon; +}; + +static const struct wlr_addon_interface addon_interface; + +static struct scene_descriptor *scene_node_get_descriptor( + struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { + struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface); + if (!addon) { + return NULL; + } + + struct scene_descriptor *desc = wl_container_of(addon, desc, addon); + return desc; +} + +static void descriptor_destroy(struct scene_descriptor *desc) { + wlr_addon_finish(&desc->addon); + free(desc); +} + +void *scene_descriptor_try_get(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type) { + struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return NULL; + } + + return desc->data; +} + +void scene_descriptor_destroy(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type) { + struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return; + } + descriptor_destroy(desc); +} + +static void addon_handle_destroy(struct wlr_addon *addon) { + struct scene_descriptor *desc = wl_container_of(addon, desc, addon); + descriptor_destroy(desc); +} + +static const struct wlr_addon_interface addon_interface = { + .name = "sway_scene_descriptor", + .destroy = addon_handle_destroy, +}; + +bool scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data) { + struct scene_descriptor *desc = calloc(1, sizeof(*desc)); + if (!desc) { + sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); + return false; + } + + wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface); + desc->data = data; + return true; +} diff --git a/sway/server.c b/sway/server.c index c4b98d22..a8e3f1a4 100644 --- a/sway/server.c +++ b/sway/server.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -9,25 +8,32 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -39,6 +45,7 @@ #include #include #include +#include #include "config.h" #include "list.h" #include "log.h" @@ -50,7 +57,7 @@ #include "sway/input/cursor.h" #include "sway/tree/root.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #include "sway/xwayland.h" #endif @@ -59,8 +66,11 @@ #include #endif -#define SWAY_XDG_SHELL_VERSION 2 +#define SWAY_XDG_SHELL_VERSION 5 #define SWAY_LAYER_SHELL_VERSION 4 +#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 + +bool allow_unsupported_gpu = false; #if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { @@ -92,6 +102,7 @@ static bool is_privileged(const struct wl_global *global) { global == server.output_manager_v1->global || global == server.output_power_manager_v1->global || global == server.input_method->global || + global == server.foreign_toplevel_list->global || global == server.foreign_toplevel_manager->global || global == server.data_control_manager_v1->global || global == server.screencopy_manager_v1->global || @@ -100,15 +111,16 @@ static bool is_privileged(const struct wl_global *global) { global == server.gamma_control_manager_v1->global || global == server.layer_shell->global || global == server.session_lock.manager->global || - global == server.input->inhibit->global || global == server.input->keyboard_shortcuts_inhibit->global || global == server.input->virtual_keyboard->global || - global == server.input->virtual_pointer->global; + global == server.input->virtual_pointer->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; @@ -127,6 +139,81 @@ static bool filter_global(const struct wl_client *client, return true; } +static void detect_proprietary(struct wlr_backend *backend, void *data) { + int drm_fd = wlr_backend_get_drm_fd(backend); + if (drm_fd < 0) { + return; + } + + drmVersion *version = drmGetVersion(drm_fd); + if (version == NULL) { + sway_log(SWAY_ERROR, "drmGetVersion() failed"); + return; + } + + bool is_unsupported = false; + if (strcmp(version->name, "nvidia-drm") == 0) { + is_unsupported = true; + sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); + if (!allow_unsupported_gpu) { + sway_log(SWAY_ERROR, "Use Nouveau instead"); + } + } + + if (strcmp(version->name, "evdi") == 0) { + is_unsupported = true; + sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); + } + + if (!allow_unsupported_gpu && is_unsupported) { + sway_log(SWAY_ERROR, + "Proprietary drivers are NOT supported. To launch sway anyway, " + "launch with --unsupported-gpu and DO NOT report issues."); + exit(EXIT_FAILURE); + } + + drmFreeVersion(version); +} + +static void handle_renderer_lost(struct wl_listener *listener, void *data) { + struct sway_server *server = wl_container_of(listener, server, renderer_lost); + + sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); + + struct wlr_renderer *renderer = fx_renderer_create(server->backend); + if (renderer == NULL) { + sway_log(SWAY_ERROR, "Unable to create renderer"); + return; + } + + struct wlr_allocator *allocator = + wlr_allocator_autocreate(server->backend, renderer); + if (allocator == NULL) { + sway_log(SWAY_ERROR, "Unable to create allocator"); + wlr_renderer_destroy(renderer); + return; + } + + struct wlr_renderer *old_renderer = server->renderer; + struct wlr_allocator *old_allocator = server->allocator; + server->renderer = renderer; + server->allocator = allocator; + + wl_list_remove(&server->renderer_lost.link); + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + + wlr_compositor_set_renderer(server->compositor, renderer); + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + wlr_output_init_render(output->wlr_output, + server->allocator, server->renderer); + } + + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); @@ -134,24 +221,33 @@ bool server_init(struct sway_server *server) { wl_display_set_global_filter(server->wl_display, filter_global, NULL); - server->backend = wlr_backend_autocreate(server->wl_display, &server->session); + root = root_create(server->wl_display); + + server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); if (!server->backend) { sway_log(SWAY_ERROR, "Unable to create backend"); return false; } + wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); + server->renderer = fx_renderer_create(server->backend); if (!server->renderer) { - sway_log(SWAY_ERROR, "Failed to create fx_renderer"); + sway_log(SWAY_ERROR, "Failed to create renderer"); return false; } + server->renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { - wlr_drm_create(server->wl_display, server->renderer); + if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 4, server->renderer); + if (debug.legacy_wl_drm) { + wlr_drm_create(server->wl_display, server->renderer); + } } server->allocator = wlr_allocator_autocreate(server->backend, @@ -163,9 +259,6 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create(server->wl_display, 6, server->renderer); - server->compositor_new_surface.notify = handle_compositor_new_surface; - wl_signal_add(&server->compositor->events.new_surface, - &server->compositor_new_surface); wlr_subcompositor_create(server->wl_display); @@ -180,11 +273,9 @@ bool server_init(struct sway_server *server) { server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); - server->output_layout_change.notify = handle_output_layout_change; - 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(); @@ -197,9 +288,9 @@ bool server_init(struct sway_server *server) { server->xdg_shell = wlr_xdg_shell_create(server->wl_display, SWAY_XDG_SHELL_VERSION); - wl_signal_add(&server->xdg_shell->events.new_surface, - &server->xdg_shell_surface); - server->xdg_shell_surface.notify = handle_xdg_shell_surface; + wl_signal_add(&server->xdg_shell->events.new_toplevel, + &server->xdg_shell_toplevel); + server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); @@ -230,8 +321,8 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); - server->presentation = - wlr_presentation_create(server->wl_display, server->backend); + wlr_presentation_create(server->wl_display, server->backend); + wlr_alpha_modifier_v1_create(server->wl_display); server->output_manager_v1 = wlr_output_manager_v1_create(server->wl_display); @@ -250,6 +341,8 @@ bool server_init(struct sway_server *server) { &server->output_power_manager_set_mode); server->input_method = wlr_input_method_manager_v2_create(server->wl_display); server->text_input = wlr_text_input_manager_v3_create(server->wl_display); + server->foreign_toplevel_list = + wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); @@ -278,6 +371,13 @@ bool server_init(struct sway_server *server) { wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + server->tearing_control_v1 = + wlr_tearing_control_manager_v1_create(server->wl_display, 1); + server->tearing_control_new_object.notify = handle_new_tearing_hint; + wl_signal_add(&server->tearing_control_v1->events.new_object, + &server->tearing_control_new_object); + wl_list_init(&server->tearing_controllers); + struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); @@ -316,7 +416,7 @@ bool server_init(struct sway_server *server) { return false; } - server->headless_backend = wlr_headless_backend_create(server->wl_display); + server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); if (!server->headless_backend) { sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); wlr_backend_destroy(server->backend); @@ -345,16 +445,17 @@ 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); + wlr_backend_destroy(server->backend); wl_display_destroy(server->wl_display); list_free(server->dirty_nodes); } 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 bcfbccf8..185ea31c 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -230,11 +230,6 @@ following properties: : string : The transform currently in use for the output. This can be _normal_, _90_, _180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_ -|- layer_shell_surfaces -: array -: An array of all layer-shell surfaces attached to the output. Each object - contains _namespace_, _layer_, _effects_, and _extent_ object that contains _x_, _y_ - _width_, and _height_ |- current_workspace : string : The workspace currently visible on the output or _null_ for disabled outputs @@ -274,8 +269,8 @@ following properties: "x": 0, "y": 0 }, - "effects": [ - ] + "effects": { + } }, { "namespace": "waybar", @@ -286,11 +281,13 @@ following properties: "x": 6, "y": 6 }, - "effects": [ - "blur", - "shadow", - "corner_rounding" - ] + "effects": { + "blur": true, + "blur_xray": false, + "blur_ignore_transparent": false, + "shadows": true, + "corner_radius": 20 + } } ], "current_workspace": "1", @@ -410,6 +407,12 @@ node and will have the following properties: : integer : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means full workspace, and 2 means global fullscreen +|- floating +: string +: Floating state of container. Can be either "auto_off" or "user_on" +|- scratchpad_state +: string +: Whether the window is in the scratchpad. Can be either "none" or "fresh" |- app_id : string : (Only views) For an xdg-shell view, the name of the application, if set. @@ -1074,7 +1077,7 @@ An object with a single string property containing the contents of the config *Example Reply:* ``` { - "config": "set $mod Mod4\nbindsym $mod+q exit\n" + "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" } ``` @@ -1196,7 +1199,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_ @@ -1218,6 +1221,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 028cb7ab..dc16c257 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -72,13 +72,11 @@ must be separated by one space. For example: *output* scale Scales the specified output by the specified scale _factor_. An integer is - recommended, but fractional values are also supported. If a fractional - value are specified, be warned that it is not possible to faithfully - represent the contents of your windows - they will be rendered at the next - highest integer scale factor and downscaled. You may be better served by - setting an integer scale factor and adjusting the font size of your - applications to taste. HiDPI isn't supported with Xwayland clients (windows - will blur). + recommended, but fractional values are also supported. You may be better + served by setting an integer scale factor and adjusting the font size of + your applications to taste. HiDPI isn't supported with Xwayland clients + (windows will blur). A fractional scale may be slightly adjusted to match + requirements of the protocol. *output* scale_filter linear|nearest|smart Indicates how to scale application buffers that are rendered at a scale @@ -156,7 +154,7 @@ must be separated by one space. For example: This setting only has an effect on Wayland and DRM backends, as support for presentation timestamps and predicted output refresh rate is required. -*output* adaptive_sync on|off +*output* adaptive_sync on|off|toggle Enables or disables adaptive synchronization (often referred to as Variable Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). @@ -165,9 +163,9 @@ must be separated by one space. For example: adaptive sync can improve latency, but can cause flickering on some hardware. -*output* render_bit_depth 8|10 - Controls the color channel bit depth at which frames are rendered; the - default is currently 8 bits per channel. +*output* render_bit_depth 6|8|10 + Controls the maximum color channel bit depth at which frames are + rendered; the default is currently 8 bits per channel. Setting higher values will not have an effect if hardware and software lack support for such bit depths. Successfully increasing the render bit depth @@ -180,6 +178,38 @@ 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. + +*output* allow_tearing yes|no + Allows or disallows screen tearing as a result of immediate page flips, + and an immediate presentation mode from a client. The default is that no + screen tearing is allowed. + + With immediate page flips, frames from the client are presented as soon + as possible instead of synchronizing with the monitor's vblank interval + (VSync). + + It is recommended to set *max_render_time* to *off*. In that case a page flip + happens as soon as a client updates. Otherwise, tearing will only happen if + rendering takes longer than the configured milliseconds before the next + display refresh. + + To adjust whether tearing is allowed for specific applications, see + *allow_tearing* in *sway*(5). Note that tearing will only be enabled + when it's allowed for both the output and the application. + + This setting only has effect when a window is fullscreen on the output. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 10ae1e0a..a0d8b0cf 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -57,6 +57,7 @@ which you may only select one. *[...]* is used for optional arguments, and This section only lists general commands. For input and output commands, refer to *sway-input*(5) and *sway-output*(5). +## Config only commands The following commands may only be used in the configuration file. *bar* [] @@ -102,6 +103,7 @@ The following commands may only be used in the configuration file. machines, it may be desirable to have Xwayland started immediately by using _force_ instead of _enable_. +## Runtime only commands The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -219,6 +221,20 @@ set|plus|minus|toggle effect on the output the window is currently on. See *sway-output*(5) for further details. +*allow_tearing* yes|no + Allows or disallows screen tearing as a result of immediate page flips + for a fullscreen application. + + When this option is not set, the tearing hints provided by the application + determine whether tearing is allowed. When _yes_ is specified, + the application allows tearing regardless of the tearing hints. + When _no_ is specified, tearing will never be allowed on the application, + regardless of the tearing hints. + + This setting only has an effect if tearing is allowed on the output through + the per-output *allow_tearing* setting. See *sway-output*(5) + for further details. + *move* left|right|up|down [ px] Moves the focused container in the direction specified. The optional _px_ argument specifies how many pixels to move the container. If unspecified, @@ -375,6 +391,7 @@ set|plus|minus|toggle The default format is "%title". +## Config or runtime commands The following commands may be used either in the configuration file or at runtime. @@ -404,6 +421,12 @@ runtime. only be available for that group. By default, if you overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. + For specifying modifier keys, you can use the XKB modifier names _Shift_, + _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), + _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for + AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_ + (for Alt), and _Super_ (for the Logo key). + Unless the flag _--locked_ is set, the command will not be run when a screen locking program is active. If there is a matching binding with and without _--locked_, the one with will be preferred when locked and the @@ -709,7 +732,7 @@ The default colors are: while 5 is the default value. *blur_noise* - Adjusts the percentage of noise applied to the blur between 0 and 1 + Adjusts the percentage of noise applied to the blur between 0 and 1 while 0.02 (2%) is the default value. *blur_brightness* @@ -819,17 +842,26 @@ The default colors are: Effects: - *blur* + - *blur_xray* - *blur_ignore_transparent* - *shadows* - *corner_radius* + - *reset* Resets all previously applied effects + - *reset* To reset/disable all previously applied effects to the layer application Example: - layer_effects "waybar" blur enable; shadows enable; corner_radius 6 + layer_effects "waybar" { + blur enable; + blur_xray enable; + blur_ignore_transparent enable; + shadows enable; + corner_radius 6; + } - SwayIPC Example: + SwayIPC Example (you can only set one effect at a time through `swaymsg`): - swaymsg "layer_effects 'waybar' 'blur enable; shadows enable'" + swaymsg layer_effects "waybar" "blur enable" *gaps* inner|outer|horizontal|vertical|top|right|bottom|left Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner @@ -931,11 +963,6 @@ The default colors are: Adjusts the opacity of the window between 0 (completely transparent) and 1 (completely opaque). If the operation is omitted, _set_ will be used. -*saturation* [set|plus|minus] - Adjusts the saturation (Digital Vibrance) of the window between 0 (black and - white) and 2 (over saturated which is suited for some FPS games) while 1 is - the default saturation. If the operation is omitted, _set_ will be used. - *tiling_drag* enable|disable|toggle Sets whether or not tiling containers can be dragged with the mouse. If _enabled_ (default), the _floating_mod_ can be used to drag tiling, as well @@ -1144,4 +1171,3 @@ The following attributes may be matched with: # SEE ALSO *sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) - diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c new file mode 100644 index 00000000..7c781355 --- /dev/null +++ b/sway/sway_text_node.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include +#include +#include +#include "cairo_util.h" +#include "log.h" +#include "pango.h" +#include "sway/config.h" +#include "sway/sway_text_node.h" + +struct cairo_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; + cairo_t *cairo; +}; + +static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + + cairo_surface_destroy(buffer->surface); + cairo_destroy(buffer->cairo); + free(buffer); +} + +static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + *data = cairo_image_surface_get_data(buffer->surface); + *stride = cairo_image_surface_get_stride(buffer->surface); + *format = DRM_FORMAT_ARGB8888; + return true; +} + +static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static const struct wlr_buffer_impl cairo_buffer_impl = { + .destroy = cairo_buffer_handle_destroy, + .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, + .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, +}; + +struct text_buffer { + struct wlr_scene_buffer *buffer_node; + char *text; + struct sway_text_node props; + + bool visible; + float scale; + enum wl_output_subpixel subpixel; + + struct wl_listener outputs_update; + struct wl_listener destroy; +}; + +static int get_text_width(struct sway_text_node *props) { + int width = props->width; + if (props->max_width >= 0) { + width = MIN(width, props->max_width); + } + return MAX(width, 0); +} + +static void update_source_box(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + struct wlr_fbox source_box = { + .x = 0, + .y = 0, + .width = ceil(get_text_width(props) * buffer->scale), + .height = ceil(props->height * buffer->scale), + }; + + wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); +} + +static void render_backing_buffer(struct text_buffer *buffer) { + if (!buffer->visible) { + return; + } + + if (buffer->props.max_width == 0) { + wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL); + return; + } + + float scale = buffer->scale; + int width = ceil(buffer->props.width * scale); + int height = ceil(buffer->props.height * scale); + float *color = (float *)&buffer->props.color; + float *background = (float *)&buffer->props.background; + PangoContext *pango = NULL; + + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + enum wl_output_subpixel subpixel = buffer->subpixel; + if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); + } else { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); + } + + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width, height); + cairo_status_t status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", + cairo_status_to_string(status)); + goto err; + } + + struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer)); + if (!cairo_buffer) { + sway_log(SWAY_ERROR, "cairo_buffer allocation failed"); + goto err; + } + + cairo_t *cairo = cairo_create(surface); + if (!cairo) { + sway_log(SWAY_ERROR, "cairo_create failed"); + free(cairo_buffer); + goto err; + } + + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_set_font_options(cairo, fo); + pango = pango_cairo_create_context(cairo); + + cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]); + cairo_rectangle(cairo, 0, 0, width, height); + cairo_fill(cairo); + + cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); + cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); + + render_text(cairo, config->font_description, scale, buffer->props.pango_markup, + "%s", buffer->text); + + cairo_surface_flush(surface); + + wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); + cairo_buffer->surface = surface; + cairo_buffer->cairo = cairo; + + wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); + wlr_buffer_drop(&cairo_buffer->base); + update_source_box(buffer); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + if (background[3] == 1) { + pixman_region32_union_rect(&opaque, &opaque, 0, 0, + buffer->props.width, buffer->props.height); + } + wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); + pixman_region32_fini(&opaque); + +err: + if (pango) g_object_unref(pango); + cairo_font_options_destroy(fo); +} + +static void handle_outputs_update(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update); + struct wlr_scene_outputs_update_event *event = data; + + float scale = 0; + enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *output = event->active[i]; + if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + subpixel = output->output->subpixel; + } else if (subpixel != output->output->subpixel) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale != 0 && scale != output->output->scale) { + // drop down to gray scale if we encounter outputs with different + // scales or else we will have chromatic aberations + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale < output->output->scale) { + scale = output->output->scale; + } + } + + buffer->visible = event->size > 0; + + if (scale != buffer->scale || subpixel != buffer->subpixel) { + buffer->scale = scale; + buffer->subpixel = subpixel; + render_backing_buffer(buffer); + } +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); + + wl_list_remove(&buffer->outputs_update.link); + wl_list_remove(&buffer->destroy.link); + + free(buffer->text); + free(buffer); +} + +static void text_calc_size(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + + cairo_t *c = cairo_create(NULL); + if (!c) { + sway_log(SWAY_ERROR, "cairo_t allocation failed"); + return; + } + + cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); + get_text_size(c, config->font_description, &props->width, NULL, + &props->baseline, 1, props->pango_markup, "%s", buffer->text); + cairo_destroy(c); + + wlr_scene_buffer_set_dest_size(buffer->buffer_node, + get_text_width(props), props->height); +} + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, float color[4], bool pango_markup) { + struct text_buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) { + return NULL; + } + + struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); + if (!node) { + free(buffer); + return NULL; + } + + buffer->buffer_node = node; + buffer->props.node = &node->node; + buffer->props.max_width = -1; + buffer->text = strdup(text); + if (!buffer->text) { + free(buffer); + wlr_scene_node_destroy(&node->node); + return NULL; + } + + buffer->props.height = config->font_height; + buffer->props.pango_markup = pango_markup; + memcpy(&buffer->props.color, color, sizeof(*color) * 4); + + buffer->destroy.notify = handle_destroy; + wl_signal_add(&node->node.events.destroy, &buffer->destroy); + buffer->outputs_update.notify = handle_outputs_update; + wl_signal_add(&node->events.outputs_update, &buffer->outputs_update); + + text_calc_size(buffer); + + return &buffer->props; +} + +void sway_text_node_set_color(struct sway_text_node *node, float color[4]) { + if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) { + return; + } + + memcpy(&node->color, color, sizeof(*color) * 4); + struct text_buffer *buffer = wl_container_of(node, buffer, props); + + render_backing_buffer(buffer); +} + +void sway_text_node_set_text(struct sway_text_node *node, char *text) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (strcmp(buffer->text, text) == 0) { + return; + } + + char *new_text = strdup(text); + if (!new_text) { + return; + } + + free(buffer->text); + buffer->text = new_text; + + text_calc_size(buffer); + render_backing_buffer(buffer); +} + +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); + update_source_box(buffer); + render_backing_buffer(buffer); +} + +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/swaynag.c b/sway/swaynag.c index 6031174d..bc5e23ea 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index af925d05..81135bda 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -315,18 +314,17 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, - output->wlr_output, &output_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; - + if (!output->wlr_output->enabled) { + return; + } for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); } + + int output_width, output_height; + wlr_output_effective_resolution(output->wlr_output, &output_width, &output_height); + wlr_scene_optimized_blur_set_size(output->layers.blur_layer, output_width, output_height); } void arrange_root(void) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 2de58a26..55d0913a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1,32 +1,81 @@ -#define _POSIX_C_SOURCE 200809L +#include "sway/tree/container.h" #include #include #include #include #include +#include #include #include #include +#include #include "linux-dmabuf-unstable-v1-protocol.h" -#include "cairo_util.h" -#include "pango.h" +#include "scenefx/types/fx/corner_location.h" #include "sway/config.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/scene_descriptor.h" +#include "sway/sway_text_node.h" #include "sway/output.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "sway/xdg_decoration.h" #include "list.h" +#include "pango.h" #include "log.h" #include "stringop.h" +static void handle_output_enter( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_enter); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_enter( + con->view->foreign_toplevel, output->output); + } +} + +static void handle_output_leave( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_leave); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_leave( + con->view->foreign_toplevel, output->output); + } +} + +static bool handle_point_accepts_input( + struct wlr_scene_buffer *buffer, double *x, double *y) { + return false; +} + +static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, + bool *failed) { + if (*failed) { + return NULL; + } + + // just pass in random values. These will be overwritten when + // they need to be used. + struct wlr_scene_rect *rect = wlr_scene_rect_create( + parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); + if (!rect) { + sway_log(SWAY_ERROR, "Failed to allocate a wlr_scene_rect"); + *failed = true; + } + + return rect; +} + struct sway_container *container_create(struct sway_view *view) { struct sway_container *c = calloc(1, sizeof(struct sway_container)); if (!c) { @@ -34,28 +83,432 @@ struct sway_container *container_create(struct sway_view *view) { return NULL; } node_init(&c->node, N_CONTAINER, c); - c->pending.layout = L_NONE; - c->view = view; - c->alpha = 1.0f; - c->saturation = 1.0f; - c->dim = config->default_dim_inactive; - c->shadow_enabled = config->shadow_enabled; - c->blur_enabled = config->blur_enabled; - c->corner_radius = config->corner_radius; + + // Container tree structure + // - scene tree + // - shadow + // - title bar + // - border + // - background + // - title text + // - marks text + // - border + // - border top/bottom/left/right + // - content_tree (we put the content node here so when we disable the + // border everything gets disabled. We only render the content iff there + // is a border as well) + // - buffer used for output enter/leave events for foreign_toplevel + bool failed = false; + c->scene_tree = alloc_scene_tree(root->staging, &failed); + + c->shadow = alloc_scene_shadow(c->scene_tree, 0, 0, + 0, config->shadow_blur_sigma, config->shadow_color, &failed); + + c->title_bar.tree = alloc_scene_tree(c->scene_tree, &failed); + + c->border.tree = alloc_scene_tree(c->scene_tree, &failed); + c->content_tree = alloc_scene_tree(c->border.tree, &failed); + + c->title_bar.border = alloc_rect_node(c->title_bar.tree, &failed); + c->title_bar.background = alloc_rect_node(c->title_bar.tree, &failed); + + if (view) { + // only containers with views can have borders + c->border.top = alloc_rect_node(c->border.tree, &failed); + c->border.bottom = alloc_rect_node(c->border.tree, &failed); + c->border.left = alloc_rect_node(c->border.tree, &failed); + c->border.right = alloc_rect_node(c->border.tree, &failed); + + c->dim_rect = alloc_rect_node(c->border.tree, &failed); + // Disable rect input + c->dim_rect->accepts_input = false; + + c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); + if (!c->output_handler) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + failed = true; + } + + if (!failed) { + c->output_enter.notify = handle_output_enter; + wl_signal_add(&c->output_handler->events.output_enter, + &c->output_enter); + c->output_leave.notify = handle_output_leave; + wl_signal_add(&c->output_handler->events.output_leave, + &c->output_leave); + c->output_handler->point_accepts_input = handle_point_accepts_input; + } + } + + if (!failed && !scene_descriptor_assign(&c->scene_tree->node, + SWAY_SCENE_DESC_CONTAINER, c)) { + failed = true; + } + + if (failed) { + wlr_scene_node_destroy(&c->scene_tree->node); + free(c); + return NULL; + } if (!view) { c->pending.children = create_list(); c->current.children = create_list(); } + + c->pending.layout = L_NONE; + c->view = view; + c->alpha = 1.0f; c->marks = create_list(); - c->outputs = create_list(); + c->corner_radius = config->corner_radius; + c->blur_enabled = config->blur_enabled; + c->shadow_enabled = config->shadow_enabled; + c->dim = config->default_dim_inactive; wl_signal_init(&c->events.destroy); wl_signal_emit_mutable(&root->events.new_node, &c->node); + container_update(c); + return c; } +static bool container_is_focused(struct sway_container *con, void *data) { + return con->current.focused; +} + +static bool container_has_focused_child(struct sway_container *con) { + return container_find_child(con, container_is_focused, NULL); +} + +static bool container_is_current_parent_focused(struct sway_container *con) { + if (con->current.parent) { + struct sway_container *parent = con->current.parent; + return parent->current.focused || container_is_current_parent_focused(parent); + } else if (con->current.workspace) { + struct sway_workspace *ws = con->current.workspace; + return ws->current.focused; + } + + return false; +} + +static struct border_colors *container_get_current_colors( + struct sway_container *con) { + struct border_colors *colors; + + bool urgent = con->view ? + view_is_urgent(con->view) : container_has_urgent_child(con); + struct sway_container *active_child; + + if (con->current.parent) { + active_child = con->current.parent->current.focused_inactive_child; + } else if (con->current.workspace) { + active_child = con->current.workspace->current.focused_inactive_child; + } else { + active_child = NULL; + } + + if (urgent) { + colors = &config->border_colors.urgent; + } else if (con->current.focused || container_is_current_parent_focused(con)) { + colors = &config->border_colors.focused; + } else if (config->has_focused_tab_title && container_has_focused_child(con)) { + colors = &config->border_colors.focused_tab_title; + } else if (con == active_child) { + colors = &config->border_colors.focused_inactive; + } else { + colors = &config->border_colors.unfocused; + } + + return colors; +} + +static bool container_is_current_floating(struct sway_container *container) { + if (!container->current.parent && container->current.workspace && + list_find(container->current.workspace->floating, container) != -1) { + return true; + } + if (container->scratchpad) { + return true; + } + return false; +} + +// scene rect wants premultiplied colors +static void scene_rect_set_color(struct wlr_scene_rect *rect, + const float color[4], float opacity) { + const float premultiplied[] = { + color[0] * color[3] * opacity, + color[1] * color[3] * opacity, + color[2] * color[3] * opacity, + color[3] * opacity, + }; + + wlr_scene_rect_set_color(rect, premultiplied); +} + +void container_update(struct sway_container *con) { + struct border_colors *colors = container_get_current_colors(con); + list_t *siblings = NULL; + enum sway_container_layout layout = L_NONE; + float alpha = con->alpha; + + if (con->current.parent) { + siblings = con->current.parent->current.children; + layout = con->current.parent->current.layout; + } else if (con->current.workspace) { + siblings = con->current.workspace->current.tiling; + layout = con->current.workspace->current.layout; + } + + float bottom[4], right[4]; + memcpy(bottom, colors->child_border, sizeof(bottom)); + memcpy(right, colors->child_border, sizeof(right)); + + if (!container_is_current_floating(con) && siblings && siblings->length == 1) { + if (layout == L_HORIZ) { + memcpy(right, colors->indicator, sizeof(right)); + } else if (layout == L_VERT) { + memcpy(bottom, colors->indicator, sizeof(bottom)); + } + } + + scene_rect_set_color(con->title_bar.background, colors->background, alpha); + scene_rect_set_color(con->title_bar.border, colors->border, alpha); + + if (con->view) { + scene_rect_set_color(con->border.top, colors->child_border, alpha); + scene_rect_set_color(con->border.bottom, bottom, alpha); + scene_rect_set_color(con->border.left, colors->child_border, alpha); + scene_rect_set_color(con->border.right, right, alpha); + } + + if (con->title_bar.title_text) { + sway_text_node_set_color(con->title_bar.title_text, colors->text); + sway_text_node_set_background(con->title_bar.title_text, colors->background); + } + + if (con->title_bar.marks_text) { + sway_text_node_set_color(con->title_bar.marks_text, colors->text); + sway_text_node_set_background(con->title_bar.marks_text, colors->background); + } + + if (con->dim_rect) { + float *color = config->dim_inactive_colors.unfocused; + + // Urgency + if (con->view && view_is_urgent(con->view)) { + color = config->dim_inactive_colors.urgent; + } + // Focused + bool focused = con->current.focused || container_is_current_parent_focused(con); + + scene_rect_set_color(con->dim_rect, color, focused ? 0.0 : con->dim); + } +} + +void container_update_itself_and_parents(struct sway_container *con) { + container_update(con); + + if (con->current.parent) { + container_update_itself_and_parents(con->current.parent); + } +} + +void container_arrange_title_bar(struct sway_container *con) { + enum alignment title_align = config->title_align; + int marks_buffer_width = 0; + int width = con->title_width; + int height = container_titlebar_height(); + + struct wlr_box text_box = { 0, 0, 0, 0 }; + + if (con->title_bar.marks_text) { + struct sway_text_node *node = con->title_bar.marks_text; + marks_buffer_width = node->width; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = config->titlebar_h_padding; + } else { + h_padding = width - config->titlebar_h_padding - marks_buffer_width; + } + + h_padding = MAX(h_padding, config->titlebar_h_padding); + + int alloc_width = MIN((int)node->width, + width - h_padding - config->titlebar_h_padding); + alloc_width = MAX(alloc_width, 0); + + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + text_box.x = node->node->x; + text_box.y = node->node->y; + text_box.width = alloc_width; + text_box.height = node->height; + } + + if (con->title_bar.title_text) { + struct sway_text_node *node = con->title_bar.title_text; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = width - config->titlebar_h_padding - node->width; + } else if (title_align == ALIGN_CENTER) { + h_padding = ((int)width - marks_buffer_width - node->width) >> 1; + } else { + h_padding = config->titlebar_h_padding; + } + + h_padding = MAX(h_padding, config->titlebar_h_padding); + + int alloc_width = MIN((int) node->width, + width - h_padding - config->titlebar_h_padding); + alloc_width = MAX(alloc_width, 0); + + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + text_box.x = MAX(text_box.x, node->node->x); + text_box.y = MAX(text_box.y, node->node->y); + text_box.width = MAX(text_box.width, alloc_width); + text_box.height = MAX(text_box.height, node->height); + } + + if (width <= 0 || height <= 0) { + return; + } + + int thickness = config->titlebar_border_thickness; + int background_corner_radius = container_has_corner_radius(con) ? + con->corner_radius + con->current.border_thickness - thickness : 0; + enum corner_location corners = CORNER_LOCATION_TOP; + + if (con->current.parent) { + list_t *siblings = con->current.parent->current.children; + if (con->current.parent->current.layout == L_TABBED && siblings->length > 1) { + if (siblings->items[0] == con) { + corners = CORNER_LOCATION_TOP_LEFT; + } else if (siblings->items[siblings->length - 1] == con) { + corners = CORNER_LOCATION_TOP_RIGHT; + } else { + background_corner_radius = 0; + corners = CORNER_LOCATION_NONE; + } + } else if (con->current.parent->current.layout == L_STACKED && + siblings->items[0] != con) { + background_corner_radius = 0; + corners = CORNER_LOCATION_NONE; + } + } + + wlr_scene_node_set_position(&con->title_bar.background->node, thickness, thickness); + wlr_scene_rect_set_size(con->title_bar.background, width - thickness * 2, + height - thickness * (config->titlebar_separator ? 2 : 1)); + wlr_scene_rect_set_corner_radius(con->title_bar.background, background_corner_radius, corners); + + text_box.x -= thickness; + text_box.y -= thickness; + wlr_scene_rect_set_clipped_region(con->title_bar.background, (struct clipped_region) { + .corner_radius = 0, + .corners = CORNER_LOCATION_NONE, + .area = text_box, + }); + + wlr_scene_rect_set_size(con->title_bar.border, width, height); + wlr_scene_rect_set_corner_radius(con->title_bar.border, background_corner_radius ? + background_corner_radius + thickness : 0, corners); + wlr_scene_rect_set_clipped_region(con->title_bar.border, (struct clipped_region) { + .corner_radius = background_corner_radius, + .corners = corners, + .area = { + .x = thickness, + .y = thickness, + .width = con->title_bar.background->width, + .height = con->title_bar.background->height, + }, + }); + + container_update(con); +} + +void container_update_marks(struct sway_container *con) { + char *buffer = NULL; + + if (config->show_marks && con->marks->length) { + size_t len = 0; + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + len += strlen(mark) + 2; + } + } + buffer = calloc(len + 1, 1); + char *part = malloc(len + 1); + + if (!sway_assert(buffer && part, "Unable to allocate memory")) { + free(buffer); + return; + } + + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + snprintf(part, len + 1, "[%s]", mark); + strcat(buffer, part); + } + } + free(part); + } + + if (!buffer) { + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + } else if (!con->title_bar.marks_text) { + struct border_colors *colors = container_get_current_colors(con); + + con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, + buffer, colors->text, false); + } else { + sway_text_node_set_text(con->title_bar.marks_text, buffer); + } + + container_arrange_title_bar(con); + free(buffer); +} + +void container_update_title_bar(struct sway_container *con) { + if (!con->formatted_title) { + return; + } + + struct border_colors *colors = container_get_current_colors(con); + + if (con->title_bar.title_text) { + wlr_scene_node_destroy(con->title_bar.title_text->node); + con->title_bar.title_text = NULL; + } + + con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, + con->formatted_title, colors->text, config->pango_markup); + + // we always have to remake these text buffers completely for text font + // changes etc... + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + + container_update_marks(con); + container_arrange_title_bar(con); +} + void container_destroy(struct sway_container *con) { if (!sway_assert(con->node.destroying, "Tried to free container which wasn't marked as destroying")) { @@ -67,29 +520,22 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); - wlr_texture_destroy(con->title_focused); - wlr_texture_destroy(con->title_focused_inactive); - wlr_texture_destroy(con->title_unfocused); - wlr_texture_destroy(con->title_urgent); - wlr_texture_destroy(con->title_focused_tab_title); + free(con->title_format); list_free(con->pending.children); list_free(con->current.children); - list_free(con->outputs); list_free_items_and_destroy(con->marks); - wlr_texture_destroy(con->marks_focused); - wlr_texture_destroy(con->marks_focused_inactive); - wlr_texture_destroy(con->marks_unfocused); - wlr_texture_destroy(con->marks_urgent); - wlr_texture_destroy(con->marks_focused_tab_title); if (con->view && con->view->container == con) { con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); } } + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); free(con); } @@ -176,268 +622,6 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(con->view, "Expected a view")) { - return NULL; - } - struct sway_view *view = con->view; - double view_sx = lx - con->surface_x + view->geometry.x; - double view_sy = ly - con->surface_y + view->geometry.y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - switch (view->type) { -#if HAVE_XWAYLAND - case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(view->surface, - view_sx, view_sy, &_sx, &_sy); - break; -#endif - case SWAY_VIEW_XDG_SHELL: - _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_toplevel->base, - view_sx, view_sy, &_sx, &_sy); - break; - } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return con; - } - return NULL; -} - -/** - * container_at for a container with layout L_TABBED. - */ -static struct sway_container *container_at_tabbed(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - if (!children->length) { - return NULL; - } - - // Tab titles - int title_height = container_titlebar_height(); - if (ly < box.y + title_height) { - int tab_width = box.width / children->length; - int child_index = (lx - box.x) / tab_width; - if (child_index >= children->length) { - child_index = children->length - 1; - } - struct sway_container *child = children->items[child_index]; - return child; - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_STACKED. - */ -static struct sway_container *container_at_stacked(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - - // Title bars - int title_height = container_titlebar_height(); - if (title_height > 0) { - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; - } - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_HORIZ or L_VERT. - */ -static struct sway_container *container_at_linear(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - list_t *children = node_get_children(parent); - for (int i = 0; i < children->length; ++i) { - struct sway_container *child = children->items[i]; - struct sway_container *container = - tiling_container_at(&child->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - return NULL; -} - -static struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - // For outputs with floating containers that overhang the output bounds, - // those at the end of the output list appear on top of floating - // containers from other outputs, so iterate the list in reverse. - for (int i = root->outputs->length - 1; i >= 0; --i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->workspaces->length; ++j) { - struct sway_workspace *ws = output->workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - // Items at the end of the list are on top, so iterate the list in - // reverse. - for (int k = ws->floating->length - 1; k >= 0; --k) { - struct sway_container *floater = ws->floating->items[k]; - struct sway_container *container = - tiling_container_at(&floater->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - } - } - return NULL; -} - -static struct sway_container *view_container_content_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.content_x, - .y = container->pending.content_y, - .width = container->pending.content_width, - .height = container->pending.content_height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -static struct sway_container *view_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.x, - .y = container->pending.y, - .width = container->pending.width, - .height = container->pending.height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -struct sway_container *tiling_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (node_is_view(parent)) { - return view_container_at(parent, lx, ly, surface, sx, sy); - } - if (!node_get_children(parent)) { - return NULL; - } - switch (node_get_layout(parent)) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_NONE: - return NULL; - } - return NULL; -} - -static bool surface_is_popup(struct wlr_surface *surface) { - while (wlr_xdg_surface_try_from_wlr_surface(surface) == NULL) { - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(surface); - if (subsurface == NULL) { - return false; - } - surface = subsurface->parent; - } - if (wlr_input_popup_surface_v2_try_from_wlr_surface(surface) != NULL) { - return true; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_try_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL; -} - -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c; - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - bool is_floating = focus && container_is_floating_or_child(focus); - // Focused view's popups - if (focus && focus->view) { - c = surface_at_view(focus, lx, ly, surface, sx, sy); - if (c && surface_is_popup(*surface)) { - return c; - } - *surface = NULL; - } - // Floating - if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { - return c; - } - // Tiling (focused) - if (focus && focus->view && !is_floating) { - if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { - return c; - } - } - // Tiling (non-focused) - if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { - return c; - } - return NULL; -} - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { @@ -483,124 +667,89 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } -void container_damage_whole(struct sway_container *container) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, container); +static char *escape_pango_markup(const char *buffer) { + size_t length = escape_markup_text(buffer, NULL); + char *escaped_title = calloc(length + 1, sizeof(char)); + escape_markup_text(buffer, escaped_title); + return escaped_title; +} + +static size_t append_prop(char *buffer, const char *value) { + if (!value) { + return 0; + } + // If using pango_markup in font, we need to escape all markup chars + // from values to make sure tags are not inserted by clients + if (config->pango_markup) { + char *escaped_value = escape_pango_markup(value); + lenient_strcat(buffer, escaped_value); + size_t len = strlen(escaped_value); + free(escaped_value); + return len; + } else { + lenient_strcat(buffer, value); + return strlen(value); } } /** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. + * Calculate and return the length of the formatted title. + * If buffer is not NULL, also populate the buffer with the formatted title. */ -struct sway_output *container_get_effective_output(struct sway_container *con) { - if (con->outputs->length == 0) { - return NULL; - } - return con->outputs->items[con->outputs->length - 1]; -} - -static void render_titlebar_text_texture(struct sway_output *output, - struct sway_container *con, struct wlr_texture **texture, - struct border_colors *class, bool pango_markup, char *text) { - double scale = output->wlr_output->scale; - int width = 0; - int height = config->font_height * scale; - int baseline; - - // We must use a non-nil cairo_t for cairo_set_font_options to work. - // Therefore, we cannot use cairo_create(NULL). - cairo_surface_t *dummy_surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, 0, 0); - cairo_t *c = cairo_create(dummy_surface); - cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); - cairo_font_options_t *fo = cairo_font_options_create(); - if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); - } else { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_subpixel_order(fo, - to_cairo_subpixel_order(output->wlr_output->subpixel)); - } - cairo_set_font_options(c, fo); - get_text_size(c, config->font_description, &width, NULL, &baseline, scale, - config->pango_markup, "%s", text); - cairo_surface_destroy(dummy_surface); - cairo_destroy(c); - - if (width == 0 || height == 0) { - return; +size_t parse_title_format(struct sway_container *container, char *buffer) { + if (!container->title_format || strcmp(container->title_format, "%title") == 0) { + if (container->view) { + return append_prop(buffer, view_get_title(container->view)); + } else { + return container_build_representation(container->pending.layout, container->pending.children, buffer); + } } - if (height > config->font_height * scale) { - height = config->font_height * scale; + size_t len = 0; + char *format = container->title_format; + char *next = strchr(format, '%'); + while (next) { + // Copy everything up to the % + lenient_strncat(buffer, format, next - format); + len += next - format; + format = next; + + if (strncmp(next, "%title", 6) == 0) { + if (container->view) { + len += append_prop(buffer, view_get_title(container->view)); + } else { + len += container_build_representation(container->pending.layout, container->pending.children, buffer); + } + format += 6; + } else if (container->view) { + if (strncmp(next, "%app_id", 7) == 0) { + len += append_prop(buffer, view_get_app_id(container->view)); + format += 7; + } else if (strncmp(next, "%class", 6) == 0) { + len += append_prop(buffer, view_get_class(container->view)); + format += 6; + } else if (strncmp(next, "%instance", 9) == 0) { + len += append_prop(buffer, view_get_instance(container->view)); + format += 9; + } else if (strncmp(next, "%shell", 6) == 0) { + len += append_prop(buffer, view_get_shell(container->view)); + format += 6; + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; + } + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; + } + next = strchr(format, '%'); } + lenient_strcat(buffer, format); + len += strlen(format); - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, width, height); - cairo_status_t status = cairo_surface_status(surface); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", - cairo_status_to_string(status)); - return; - } - - cairo_t *cairo = cairo_create(surface); - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); - cairo_set_font_options(cairo, fo); - cairo_font_options_destroy(fo); - cairo_set_source_rgba(cairo, class->background[0], class->background[1], - class->background[2], class->background[3]); - cairo_paint(cairo); - PangoContext *pango = pango_cairo_create_context(cairo); - cairo_set_source_rgba(cairo, class->text[0], class->text[1], - class->text[2], class->text[3]); - cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - - render_text(cairo, config->font_description, scale, pango_markup, "%s", text); - - cairo_surface_flush(surface); - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_image_surface_get_stride(surface); - struct wlr_renderer *renderer = output->wlr_output->renderer; - *texture = wlr_texture_from_pixels( - renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} - -static void update_title_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->formatted_title) { - return; - } - - render_titlebar_text_texture(output, con, texture, class, - config->pango_markup, con->formatted_title); -} - -void container_update_title_textures(struct sway_container *container) { - update_title_texture(container, &container->title_focused, - &config->border_colors.focused); - update_title_texture(container, &container->title_focused_inactive, - &config->border_colors.focused_inactive); - update_title_texture(container, &container->title_unfocused, - &config->border_colors.unfocused); - update_title_texture(container, &container->title_urgent, - &config->border_colors.urgent); - update_title_texture(container, &container->title_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(container); + return len; } /** @@ -658,17 +807,21 @@ size_t container_build_representation(enum sway_container_layout layout, void container_update_representation(struct sway_container *con) { if (!con->view) { - size_t len = container_build_representation(con->pending.layout, - con->pending.children, NULL); + size_t len = parse_title_format(con, NULL); free(con->formatted_title); con->formatted_title = calloc(len + 1, sizeof(char)); if (!sway_assert(con->formatted_title, "Unable to allocate title string")) { return; } - container_build_representation(con->pending.layout, con->pending.children, - con->formatted_title); - container_update_title_textures(con); + parse_title_format(con, con->formatted_title); + + if (con->title_bar.title_text) { + sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); + container_arrange_title_bar(con); + } else { + container_update_title_bar(con); + } } if (con->pending.parent) { container_update_representation(con->pending.parent); @@ -725,11 +878,11 @@ void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, s // Fall back to centering on the workspace. container_floating_move_to_center(con); } else { - int rel_x = con->pending.x - old->x + (con->pending.width / 2); - int rel_y = con->pending.y - old->y + (con->pending.height / 2); + double rel_x = con->pending.x - old->x + (con->pending.width / 2); + double rel_y = con->pending.y - old->y + (con->pending.height / 2); - con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); - con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); + con->pending.x = new->x + (rel_x * new->width) / old->width - (con->pending.width / 2); + con->pending.y = new->y + (rel_y * new->height) / old->height - (con->pending.height / 2); sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); } @@ -954,17 +1107,6 @@ bool container_is_floating(struct sway_container *container) { return false; } -bool container_is_current_floating(struct sway_container *container) { - if (!container->current.parent && container->current.workspace && - list_find(container->current.workspace->floating, container) != -1) { - return true; - } - if (container->scratchpad) { - return true; - } - return false; -} - void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->pending.x; box->y = container->pending.y; @@ -1097,34 +1239,6 @@ static void set_fullscreen(struct sway_container *con, bool enable) { con->view->foreign_toplevel, enable); } } - - if (!server.linux_dmabuf_v1 || !con->view->surface) { - return; - } - if (!enable) { - wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, - con->view->surface, NULL); - return; - } - - if (!con->pending.workspace || !con->pending.workspace->output) { - return; - } - - struct sway_output *output = con->pending.workspace->output; - const struct wlr_linux_dmabuf_feedback_v1_init_options options = { - .main_renderer = server.renderer, - .scanout_primary_output = output->wlr_output, - }; - struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; - if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &options)) { - return; - } - - wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, - con->view->surface, &feedback); - - wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static void container_fullscreen_workspace(struct sway_container *con) { @@ -1294,72 +1408,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { return false; } -static void surface_send_enter_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct sway_output *output = data; - surface_enter_output(surface, output); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct sway_output *output = data; - surface_leave_output(surface, output); -} - -void container_discover_outputs(struct sway_container *con) { - struct wlr_box con_box = { - .x = con->current.x, - .y = con->current.y, - .width = con->current.width, - .height = con->current.height, - }; - struct sway_output *old_output = container_get_effective_output(con); - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - output_get_box(output, &output_box); - struct wlr_box intersection; - bool intersects = - wlr_box_intersection(&intersection, &con_box, &output_box); - int index = list_find(con->outputs, output); - - if (intersects && index == -1) { - // Send enter - sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_enter_iterator, output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_enter( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_add(con->outputs, output); - } else if (!intersects && index != -1) { - // Send leave - sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_leave_iterator, output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_leave( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_del(con->outputs, index); - } - } - struct sway_output *new_output = container_get_effective_output(con); - double old_scale = old_output && old_output->enabled ? - old_output->wlr_output->scale : -1; - double new_scale = new_output ? new_output->wlr_output->scale : -1; - if (old_scale != new_scale) { - container_update_title_textures(con); - container_update_marks_textures(con); - } -} - enum sway_container_layout container_parent_layout(struct sway_container *con) { if (con->pending.parent) { return con->pending.parent->pending.layout; @@ -1370,14 +1418,6 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { return L_NONE; } -enum sway_container_layout container_current_parent_layout( - struct sway_container *con) { - if (con->current.parent) { - return con->current.parent->current.layout; - } - return con->current.workspace->current.layout; -} - list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; @@ -1395,13 +1435,6 @@ int container_sibling_index(struct sway_container *child) { return list_find(container_get_siblings(child), child); } -list_t *container_get_current_siblings(struct sway_container *container) { - if (container->current.parent) { - return container->current.parent->current.children; - } - return container->current.workspace->current.tiling; -} - void container_handle_fullscreen_reparent(struct sway_container *con) { if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || con->pending.workspace->fullscreen == con) { @@ -1616,7 +1649,7 @@ bool container_find_and_unmark(char *mark) { if (strcmp(con_mark, mark) == 0) { free(con_mark); list_del(con->marks, i); - container_update_marks_textures(con); + container_update_marks(con); ipc_event_window(con, "mark"); return true; } @@ -1647,70 +1680,15 @@ void container_add_mark(struct sway_container *con, char *mark) { ipc_event_window(con, "mark"); } -static void update_marks_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->marks->length) { - return; - } - - size_t len = 0; - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - len += strlen(mark) + 2; - } - } - char *buffer = calloc(len + 1, 1); - char *part = malloc(len + 1); - - if (!sway_assert(buffer && part, "Unable to allocate memory")) { - free(buffer); - return; - } - - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - snprintf(part, len + 1, "[%s]", mark); - strcat(buffer, part); - } - } - free(part); - - render_titlebar_text_texture(output, con, texture, class, false, buffer); - - free(buffer); -} - -void container_update_marks_textures(struct sway_container *con) { - if (!config->show_marks) { - return; - } - update_marks_texture(con, &con->marks_focused, - &config->border_colors.focused); - update_marks_texture(con, &con->marks_focused_inactive, - &config->border_colors.focused_inactive); - update_marks_texture(con, &con->marks_unfocused, - &config->border_colors.unfocused); - update_marks_texture(con, &con->marks_urgent, - &config->border_colors.urgent); - update_marks_texture(con, &con->marks_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(con); -} - void container_raise_floating(struct sway_container *con) { // Bring container to front by putting it at the end of the floating list. struct sway_container *floater = container_toplevel_ancestor(con); if (container_is_floating(floater) && floater->pending.workspace) { + // it's okay to just raise the scene directly instead of waiting + // for the transaction to go through. We won't be reconfiguring + // surfaces + wlr_scene_node_raise_to_top(&floater->scene_tree->node); + list_move_to_end(floater->pending.workspace->floating, floater); node_set_dirty(&floater->pending.workspace->node); } @@ -1968,3 +1946,17 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { container_set_fullscreen(con1, fs2); } } + +bool container_has_shadow(struct sway_container *con) { + return con->shadow_enabled + && (con->current.border != B_CSD || config->shadows_on_csd_enabled); +} + +bool container_has_corner_radius(struct sway_container *con) { + if (!con) { + return false; + } + return (container_is_floating_or_child(con) || + !(config->smart_corner_radius && con->current.workspace->current_gaps.top == 0)) && + con->corner_radius; +} diff --git a/sway/tree/node.c b/sway/tree/node.c index 12361c75..ce135e02 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include "sway/output.h" #include "sway/server.h" #include "sway/tree/container.h" @@ -159,3 +158,50 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { } return false; } + +void scene_node_disown_children(struct wlr_scene_tree *tree) { + // this function can be called as part of destruction code that will be invoked + // upon an allocation failure. Let's not crash on NULL due to an allocation error. + if (!tree) { + return; + } + + struct wlr_scene_node *child, *tmp_child; + wl_list_for_each_safe(child, tmp_child, &tree->children, link) { + wlr_scene_node_reparent(child, root->staging); + } +} + +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed) { + // fallthrough + if (*failed) { + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + *failed = true; + } + + return tree; +} + +struct wlr_scene_shadow *alloc_scene_shadow(struct wlr_scene_tree *parent, + int width, int height, int corner_radius, float blur_sigma, + const float color [static 4], bool *failed) { + // fallthrough + if (*failed) { + return NULL; + } + + struct wlr_scene_shadow *shadow = wlr_scene_shadow_create( + parent, width, height, corner_radius, blur_sigma, color); + if (!shadow) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + *failed = true; + } + + return shadow; +} diff --git a/sway/tree/output.c b/sway/tree/output.c index 4aa3a7fe..1238473e 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -87,9 +86,54 @@ static void restore_workspaces(struct sway_output *output) { output_sort_workspaces(output); } +static void destroy_scene_layers(struct sway_output *output) { + wlr_scene_node_destroy(&output->fullscreen_background->node); + + scene_node_disown_children(output->layers.tiling); + scene_node_disown_children(output->layers.fullscreen); + + wlr_scene_node_destroy(&output->layers.shell_background->node); + wlr_scene_node_destroy(&output->layers.shell_bottom->node); + wlr_scene_node_destroy(&output->layers.blur_layer->node); + wlr_scene_node_destroy(&output->layers.tiling->node); + wlr_scene_node_destroy(&output->layers.fullscreen->node); + wlr_scene_node_destroy(&output->layers.shell_top->node); + wlr_scene_node_destroy(&output->layers.shell_overlay->node); + wlr_scene_node_destroy(&output->layers.session_lock->node); +} + struct sway_output *output_create(struct wlr_output *wlr_output) { struct sway_output *output = calloc(1, sizeof(struct sway_output)); node_init(&output->node, N_OUTPUT, output); + + bool failed = false; + output->layers.shell_background = alloc_scene_tree(root->staging, &failed); + output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed); + // Initialize with size 0x0, let the arrange_output set the size + output->layers.blur_layer = wlr_scene_optimized_blur_create(root->staging, 0, 0); + output->layers.tiling = alloc_scene_tree(root->staging, &failed); + output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + output->layers.shell_top = alloc_scene_tree(root->staging, &failed); + output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed); + output->layers.session_lock = alloc_scene_tree(root->staging, &failed); + + if (!failed) { + output->fullscreen_background = wlr_scene_rect_create( + output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f}); + + if (!output->fullscreen_background) { + sway_log(SWAY_ERROR, "Unable to allocate a background rect"); + failed = true; + } + } + + if (failed) { + destroy_scene_layers(output); + wlr_scene_output_destroy(output->scene_output); + free(output); + return NULL; + } + output->wlr_output = wlr_output; wlr_output->data = output; output->detected_subpixel = wlr_output->subpixel; @@ -102,11 +146,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->workspaces = create_list(); output->current.workspaces = create_list(); - size_t len = sizeof(output->layers) / sizeof(output->layers[0]); - for (size_t i = 0; i < len; ++i) { - wl_list_init(&output->layers[i]); - } - return output; } @@ -144,12 +183,7 @@ void output_enable(struct sway_output *output) { ws->layout = output_get_default_layout(output); } - input_manager_configure_xcursor(); - wl_signal_emit_mutable(&root->events.new_node, &output->node); - - arrange_layers(output); - arrange_root(); } static void evacuate_sticky(struct sway_workspace *old_ws, @@ -238,20 +272,14 @@ void output_destroy(struct sway_output *output) { "which is still referenced by transactions")) { return; } + + destroy_scene_layers(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); } -static void untrack_output(struct sway_container *con, void *data) { - struct sway_output *output = data; - int index = list_find(con->outputs, output); - if (index != -1) { - list_del(con->outputs, index); - } -} - void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -266,18 +294,9 @@ void output_disable(struct sway_output *output) { output_evacuate(output); - root_for_each_container(untrack_output, output); - list_del(root->outputs, index); output->enabled = false; - - arrange_root(); - - // Reconfigure all devices, since devices with map_to_output directives for - // an output that goes offline should stop sending events as long as the - // output remains offline. - input_manager_configure_all_input_mappings(); } void output_begin_destroy(struct sway_output *output) { diff --git a/sway/tree/root.c b/sway/tree/root.c index 381e83de..b274f1cc 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -1,12 +1,15 @@ -#define _POSIX_C_SOURCE 200809L +#include #include #include #include +#include #include +#include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -17,49 +20,78 @@ struct sway_root *root; -static void output_layout_handle_change(struct wl_listener *listener, - void *data) { - arrange_root(); - transaction_commit_dirty(); -} - -struct sway_root *root_create(void) { +struct sway_root *root_create(struct wl_display *wl_display) { struct sway_root *root = calloc(1, sizeof(struct sway_root)); if (!root) { sway_log(SWAY_ERROR, "Unable to allocate sway_root"); return NULL; } + + struct wlr_scene *root_scene = wlr_scene_create(); + if (!root_scene) { + sway_log(SWAY_ERROR, "Unable to allocate root scene node"); + free(root); + return NULL; + } + node_init(&root->node, N_ROOT, root); - root->output_layout = wlr_output_layout_create(); - wl_list_init(&root->all_outputs); -#if HAVE_XWAYLAND - wl_list_init(&root->xwayland_unmanaged); + root->root_scene = root_scene; + + bool failed = false; + root->staging = alloc_scene_tree(&root_scene->tree, &failed); + root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed); + + root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed); + root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed); + root->layers.blur_tree = alloc_scene_tree(root->layer_tree, &failed); + root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed); + root->layers.floating = alloc_scene_tree(root->layer_tree, &failed); + 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 WLR_HAS_XWAYLAND + root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); #endif - wl_list_init(&root->drag_icons); + root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); + root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); + root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); + root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); + + if (!failed && !scene_descriptor_assign(&root->layers.seat->node, + SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) { + failed = true; + } + + if (failed) { + wlr_scene_node_destroy(&root_scene->tree.node); + free(root); + return NULL; + } + + wlr_scene_node_set_enabled(&root->staging->node, false); + + root->output_layout = wlr_output_layout_create(wl_display); + wl_list_init(&root->all_outputs); wl_signal_init(&root->events.new_node); root->outputs = create_list(); root->non_desktop_outputs = create_list(); root->scratchpad = create_list(); - root->output_layout_change.notify = output_layout_handle_change; - wl_signal_add(&root->output_layout->events.change, - &root->output_layout_change); return root; } void root_destroy(struct sway_root *root) { - wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->non_desktop_outputs); list_free(root->outputs); - wlr_output_layout_destroy(root->output_layout); + wlr_scene_node_destroy(&root->root_scene->tree.node); free(root); } /* Set minimized state from scratchpad container `show` state */ static void root_scratchpad_set_minimize(struct sway_container *con, bool minimize) { if (con->view) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(con->view->surface))) { wlr_xwayland_surface_set_minimized(xsurface, minimize); diff --git a/sway/tree/view.c b/sway/tree/view.c index 4604f480..2cc71ad9 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,22 +1,23 @@ -#define _POSIX_C_SOURCE 200809L #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" #include "log.h" #include "sway/criteria.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/launcher.h" @@ -24,26 +25,41 @@ #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/input/seat.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/surface.h" +#include "sway/sway_text_node.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "sway/config.h" #include "sway/xdg_decoration.h" -#include "pango.h" #include "stringop.h" -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { + bool failed = false; + view->scene_tree = alloc_scene_tree(root->staging, &failed); + view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + + if (!failed && !scene_descriptor_assign(&view->scene_tree->node, + SWAY_SCENE_DESC_VIEW, view)) { + failed = true; + } + + if (failed) { + wlr_scene_node_destroy(&view->scene_tree->node); + return false; + } + view->type = type; view->impl = impl; view->executed_criteria = create_list(); - wl_list_init(&view->saved_buffers); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; + view->tearing_mode = TEARING_WINDOW_HINT; wl_signal_init(&view->events.unmap); + return true; } void view_destroy(struct sway_view *view) { @@ -60,15 +76,10 @@ void view_destroy(struct sway_view *view) { return; } wl_list_remove(&view->events.unmap.listener_list); - if (!wl_list_empty(&view->saved_buffers)) { - view_remove_saved_buffer(view); - } list_free(view->executed_criteria); view_assign_ctx(view, NULL); - - free(view->title_format); - + wlr_scene_node_destroy(&view->scene_tree->node); if (view->impl->destroy) { view->impl->destroy(view); } else { @@ -114,7 +125,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); @@ -147,7 +158,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 @@ -161,9 +172,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; } } @@ -234,7 +245,7 @@ static bool view_is_only_visible(struct sway_view *view) { return true; } -bool gaps_to_edge(struct sway_view *view) { +static bool gaps_to_edge(struct sway_view *view) { struct side_gaps gaps = view->container->pending.workspace->current_gaps; return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0; } @@ -353,8 +364,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) { @@ -449,52 +460,6 @@ void view_close_popups(struct sway_view *view) { } } -void view_damage_from(struct sway_view *view) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_from_view(output, view); - } -} - -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_surface) { - view->impl->for_each_surface(view, iterator, user_data); - } else { - wlr_surface_for_each_surface(view->surface, iterator, user_data); - } -} - -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_popup_surface) { - view->impl->for_each_popup_surface(view, iterator, user_data); - } -} - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *subsurface); - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface); - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface); - -static void view_handle_surface_new_subsurface(struct wl_listener *listener, - void *data) { - struct sway_view *view = - wl_container_of(listener, view, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_subsurface_create(view, subsurface); -} - static bool view_has_executed_criteria(struct sway_view *view, struct criteria *criteria) { for (int i = 0; i < view->executed_criteria->length; ++i) { @@ -533,7 +498,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); @@ -623,6 +588,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { return NULL; } +static void update_ext_foreign_toplevel(struct sway_view *view) { + struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { + .app_id = view_get_app_id(view), + .title = view_get_title(view), + }; + wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); +} + static bool should_focus(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *prev_con = seat_get_focused_container(seat); @@ -720,6 +693,13 @@ static void handle_foreign_fullscreen_request( transaction_commit_dirty(); } +static void handle_foreign_close_request( + struct wl_listener *listener, void *data) { + struct sway_view *view = wl_container_of( + listener, view, foreign_close_request); + view_close(view); +} + static void handle_foreign_minimize( struct wl_listener *listener, void *data) { struct sway_view *view = wl_container_of( @@ -743,24 +723,17 @@ static void handle_foreign_minimize( } } -static void handle_foreign_close_request( - struct wl_listener *listener, void *data) { - struct sway_view *view = wl_container_of( - listener, view, foreign_close_request); - view_close(view); -} - static void handle_foreign_destroy( struct wl_listener *listener, void *data) { struct sway_view *view = wl_container_of( listener, view, foreign_destroy); wl_list_remove(&view->foreign_activate_request.link); - if (view->foreign_minimize.notify) { - wl_list_remove(&view->foreign_minimize.link); - } wl_list_remove(&view->foreign_fullscreen_request.link); wl_list_remove(&view->foreign_close_request.link); + if (config->scratchpad_minimize) { + wl_list_remove(&view->foreign_minimize.link); + } wl_list_remove(&view->foreign_destroy.link); } @@ -793,6 +766,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); @@ -818,6 +799,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } } + struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { + .app_id = view_get_app_id(view), + .title = view_get_title(view), + }; + view->ext_foreign_toplevel = + wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); + view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); view->foreign_activate_request.notify = handle_foreign_activate_request; @@ -829,15 +817,14 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view->foreign_close_request.notify = handle_foreign_close_request; wl_signal_add(&view->foreign_toplevel->events.request_close, &view->foreign_close_request); - view->foreign_destroy.notify = handle_foreign_destroy; - wl_signal_add(&view->foreign_toplevel->events.destroy, - &view->foreign_destroy); if (config->scratchpad_minimize) { view->foreign_minimize.notify = handle_foreign_minimize; wl_signal_add(&view->foreign_toplevel->events.request_minimize, &view->foreign_minimize); } - + view->foreign_destroy.notify = handle_foreign_destroy; + wl_signal_add(&view->foreign_toplevel->events.destroy, + &view->foreign_destroy); struct sway_container *container = view->container; if (target_sibling) { @@ -847,11 +834,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } ipc_event_window(view->container, "new"); - view_init_subsurfaces(view, wlr_surface); - wl_signal_add(&wlr_surface->events.new_subsurface, - &view->surface_new_subsurface); - view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; - if (decoration) { view_update_csd_from_client(view, decoration); } @@ -894,7 +876,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) != @@ -906,6 +888,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, input_manager_set_focus(&view->container->node); } + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } + const char *app_id; const char *class; if ((app_id = view_get_app_id(view)) != NULL) { @@ -918,8 +904,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view) { wl_signal_emit_mutable(&view->events.unmap, view); - wl_list_remove(&view->surface_new_subsurface.link); - view->executed_criteria->length = 0; if (view->urgent_timer) { @@ -927,6 +911,11 @@ void view_unmap(struct sway_view *view) { view->urgent_timer = NULL; } + if (view->ext_foreign_toplevel) { + wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); + view->ext_foreign_toplevel = NULL; + } + if (view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL; @@ -973,260 +962,38 @@ void view_update_size(struct sway_view *view) { container_set_geometry_from_content(con); } -void view_center_surface(struct sway_view *view) { +void view_center_and_clip_surface(struct sway_view *view) { struct sway_container *con = view->container; - // We always center the current coordinates rather than the next, as the - // geometry immediately affects the currently active rendering. - con->surface_x = fmax(con->current.content_x, con->current.content_x + - (con->current.content_width - view->geometry.width) / 2); - con->surface_y = fmax(con->current.content_y, con->current.content_y + - (con->current.content_height - view->geometry.height) / 2); -} -static const struct sway_view_child_impl subsurface_impl; + bool clip_to_geometry = true; -static void subsurface_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct wlr_surface *surface = child->surface; - if (child->parent && child->parent->impl && - child->parent->impl->get_view_coords) { - child->parent->impl->get_view_coords(child->parent, sx, sy); + 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 + || con->blur_enabled + || con->shadow_enabled + || con->corner_radius > 0; + + wlr_scene_node_set_position(&view->content_tree->node, x, y); } else { - *sx = *sy = 0; + wlr_scene_node_set_position(&view->content_tree->node, 0, 0); } - struct wlr_subsurface *subsurface = - wlr_subsurface_try_from_wlr_surface(surface); - *sx += subsurface->current.x; - *sy += subsurface->current.y; -} -static void subsurface_destroy(struct sway_view_child *child) { - if (!sway_assert(child->impl == &subsurface_impl, - "Expected a subsurface")) { - return; - } - struct sway_subsurface *subsurface = (struct sway_subsurface *)child; - wl_list_remove(&subsurface->destroy.link); - free(subsurface); -} - -static const struct sway_view_child_impl subsurface_impl = { - .get_view_coords = subsurface_get_view_coords, - .destroy = subsurface_destroy, -}; - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - struct sway_view_child *child = &subsurface->child; - view_child_destroy(child); -} - -static void view_child_damage(struct sway_view_child *child, bool whole); - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - view_child_init(&subsurface->child, &subsurface_impl, view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static void view_child_subsurface_create(struct sway_view_child *child, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - subsurface->child.parent = child; - wl_list_insert(&child->children, &subsurface->child.link); - view_child_init(&subsurface->child, &subsurface_impl, child->view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static bool view_child_is_mapped(struct sway_view_child *child) { - while (child) { - if (!child->mapped) { - return false; + // only make sure to clip the content if there is content to clip + if (!wl_list_empty(&con->view->content_tree->children)) { + 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, + }; } - child = child->parent; - } - return true; -} - -static void view_child_damage(struct sway_view_child *child, bool whole) { - if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { - return; - } - int sx, sy; - child->impl->get_view_coords(child, &sx, &sy); - desktop_damage_surface(child->surface, - child->view->container->pending.content_x - - child->view->geometry.x + sx, - child->view->container->pending.content_y - - child->view->geometry.y + sy, whole); -} - -static void view_child_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_commit); - view_child_damage(child, false); -} - -static void view_child_handle_surface_new_subsurface( - struct wl_listener *listener, void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_child_subsurface_create(child, subsurface); -} - -static void view_child_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_destroy); - view_child_destroy(child); -} - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_subsurface_create(view, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_subsurface_create(view, subsurface); - } -} - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } -} - -static void view_child_handle_surface_map(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_map); - child->mapped = true; - view_child_damage(child, true); -} - -static void view_child_handle_surface_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_unmap); - view_child_damage(child, true); - child->mapped = false; -} - -static void view_child_handle_view_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, view_unmap); - view_child_damage(child, true); - child->mapped = false; -} - -void view_child_init(struct sway_view_child *child, - const struct sway_view_child_impl *impl, struct sway_view *view, - struct wlr_surface *surface) { - child->impl = impl; - child->view = view; - child->surface = surface; - wl_list_init(&child->children); - - wl_signal_add(&surface->events.commit, &child->surface_commit); - child->surface_commit.notify = view_child_handle_surface_commit; - wl_signal_add(&surface->events.new_subsurface, - &child->surface_new_subsurface); - child->surface_new_subsurface.notify = - view_child_handle_surface_new_subsurface; - wl_signal_add(&surface->events.destroy, &child->surface_destroy); - child->surface_destroy.notify = view_child_handle_surface_destroy; - - // Not all child views have a map/unmap event - child->surface_map.notify = view_child_handle_surface_map; - wl_list_init(&child->surface_map.link); - child->surface_unmap.notify = view_child_handle_surface_unmap; - wl_list_init(&child->surface_unmap.link); - - wl_signal_add(&view->events.unmap, &child->view_unmap); - child->view_unmap.notify = view_child_handle_view_unmap; - - struct sway_container *container = child->view->container; - if (container != NULL) { - struct sway_workspace *workspace = container->pending.workspace; - if (workspace) { - surface_enter_output(child->surface, workspace->output); - } - } - - view_child_init_subsurfaces(child, surface); -} - -void view_child_destroy(struct sway_view_child *child) { - if (view_child_is_mapped(child) && child->view->container != NULL) { - view_child_damage(child, true); - } - - if (child->parent != NULL) { - wl_list_remove(&child->link); - child->parent = NULL; - } - - struct sway_view_child *subchild, *tmpchild; - wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { - wl_list_remove(&subchild->link); - subchild->parent = NULL; - // The subchild lost its parent link, so it cannot see that the parent - // is unmapped. Unmap it directly. - subchild->mapped = false; - } - - wl_list_remove(&child->surface_commit.link); - wl_list_remove(&child->surface_destroy.link); - wl_list_remove(&child->surface_map.link); - wl_list_remove(&child->surface_unmap.link); - wl_list_remove(&child->view_unmap.link); - wl_list_remove(&child->surface_new_subsurface.link); - - if (child->impl && child->impl->destroy) { - child->impl->destroy(child); - } else { - free(child); + wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip); } } @@ -1235,7 +1002,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); @@ -1248,9 +1015,6 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { return NULL; } - if (wlr_input_popup_surface_v2_try_from_wlr_surface(wlr_surface) != NULL) { - return NULL; - } const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; sway_log(SWAY_DEBUG, "Surface of unknown type (role %s): %p", @@ -1258,75 +1022,16 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { return NULL; } -static char *escape_pango_markup(const char *buffer) { - size_t length = escape_markup_text(buffer, NULL); - char *escaped_title = calloc(length + 1, sizeof(char)); - escape_markup_text(buffer, escaped_title); - return escaped_title; -} +void view_update_app_id(struct sway_view *view) { + const char *app_id = view_get_app_id(view); -static size_t append_prop(char *buffer, const char *value) { - if (!value) { - return 0; - } - // If using pango_markup in font, we need to escape all markup chars - // from values to make sure tags are not inserted by clients - if (config->pango_markup) { - char *escaped_value = escape_pango_markup(value); - lenient_strcat(buffer, escaped_value); - size_t len = strlen(escaped_value); - free(escaped_value); - return len; - } else { - lenient_strcat(buffer, value); - return strlen(value); - } -} - -/** - * Calculate and return the length of the formatted title. - * If buffer is not NULL, also populate the buffer with the formatted title. - */ -static size_t parse_title_format(struct sway_view *view, char *buffer) { - if (!view->title_format || strcmp(view->title_format, "%title") == 0) { - return append_prop(buffer, view_get_title(view)); + if (view->foreign_toplevel && app_id) { + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); } - size_t len = 0; - char *format = view->title_format; - char *next = strchr(format, '%'); - while (next) { - // Copy everything up to the % - lenient_strncat(buffer, format, next - format); - len += next - format; - format = next; - - if (strncmp(next, "%title", 6) == 0) { - len += append_prop(buffer, view_get_title(view)); - format += 6; - } else if (strncmp(next, "%app_id", 7) == 0) { - len += append_prop(buffer, view_get_app_id(view)); - format += 7; - } else if (strncmp(next, "%class", 6) == 0) { - len += append_prop(buffer, view_get_class(view)); - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { - len += append_prop(buffer, view_get_instance(view)); - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { - len += append_prop(buffer, view_get_shell(view)); - format += 6; - } else { - lenient_strcat(buffer, "%"); - ++format; - ++len; - } - next = strchr(format, '%'); + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); } - lenient_strcat(buffer, format); - len += strlen(format); - - return len; } void view_update_title(struct sway_view *view, bool force) { @@ -1345,7 +1050,7 @@ void view_update_title(struct sway_view *view, bool force) { free(view->container->title); free(view->container->formatted_title); - size_t len = parse_title_format(view, NULL); + size_t len = parse_title_format(view->container, NULL); if (len) { char *buffer = calloc(len + 1, sizeof(char)); @@ -1353,7 +1058,7 @@ void view_update_title(struct sway_view *view, bool force) { return; } - parse_title_format(view, buffer); + parse_title_format(view->container, buffer); view->container->formatted_title = buffer; } else { view->container->formatted_title = NULL; @@ -1362,13 +1067,23 @@ void view_update_title(struct sway_view *view, bool force) { view->container->title = title ? strdup(title) : NULL; // Update title after the global font height is updated - container_update_title_textures(view->container); + if (view->container->title_bar.title_text && len) { + sway_text_node_set_text(view->container->title_bar.title_text, + view->container->formatted_title); + container_arrange_title_bar(view->container); + } else { + container_update_title_bar(view->container); + } ipc_event_window(view->container, "title"); if (view->foreign_toplevel && title) { wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); } + + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } } bool view_is_visible(struct sway_view *view) { @@ -1429,6 +1144,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { return; } clock_gettime(CLOCK_MONOTONIC, &view->urgent); + container_update_itself_and_parents(view->container); } else { view->urgent = (struct timespec){ 0 }; if (view->urgent_timer) { @@ -1436,11 +1152,10 @@ void view_set_urgent(struct sway_view *view, bool enable) { view->urgent_timer = NULL; } } - container_damage_whole(view->container); 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); } } @@ -1450,40 +1165,60 @@ bool view_is_urgent(struct sway_view *view) { } void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { + if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { return; } - struct sway_saved_buffer *saved_buf, *tmp; - wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { - wlr_buffer_unlock(&saved_buf->buffer->base); - wl_list_remove(&saved_buf->link); - free(saved_buf); - } + + wlr_scene_node_destroy(&view->saved_surface_tree->node); + view->saved_surface_tree = NULL; + wlr_scene_node_set_enabled(&view->content_tree->node, true); } -static void view_save_buffer_iterator(struct wlr_surface *surface, +static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, int sx, int sy, void *data) { - struct sway_view *view = data; + struct wlr_scene_tree *tree = data; - if (surface && surface->buffer) { - wlr_buffer_lock(&surface->buffer->base); - struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); - saved_buffer->buffer = surface->buffer; - saved_buffer->width = surface->current.width; - saved_buffer->height = surface->current.height; - saved_buffer->x = view->container->surface_x + sx; - saved_buffer->y = view->container->surface_y + sy; - saved_buffer->transform = surface->current.transform; - wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); - wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); + struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); + if (!sbuf) { + sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); + return; } + + wlr_scene_buffer_set_dest_size(sbuf, + buffer->dst_width, buffer->dst_height); + wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); + wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); + wlr_scene_node_set_position(&sbuf->node, sx, sy); + wlr_scene_buffer_set_transform(sbuf, buffer->transform); + wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); + + // Set effects to saved views + wlr_scene_buffer_set_corner_radius(sbuf, buffer->corner_radius, buffer->corners); + wlr_scene_buffer_set_backdrop_blur(sbuf, buffer->backdrop_blur); + wlr_scene_buffer_set_backdrop_blur_optimized(sbuf, buffer->backdrop_blur_optimized); + wlr_scene_buffer_set_backdrop_blur_ignore_transparent(sbuf, buffer->backdrop_blur_ignore_transparent); } void view_save_buffer(struct sway_view *view) { - if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { + if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { view_remove_saved_buffer(view); } - view_for_each_surface(view, view_save_buffer_iterator, view); + + view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); + if (!view->saved_surface_tree) { + sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); + return; + } + + // Enable and disable the saved surface tree like so to atomitaclly update + // the tree. This will prevent over damaging or other weirdness. + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); + + wlr_scene_node_for_each_buffer(&view->content_tree->node, + view_save_buffer_iterator, view->saved_surface_tree); + + wlr_scene_node_set_enabled(&view->content_tree->node, false); + wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); } bool view_is_transient_for(struct sway_view *child, @@ -1491,3 +1226,32 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +bool view_can_tear(struct sway_view *view) { + switch (view->tearing_mode) { + case TEARING_OVERRIDE_FALSE: + return false; + case TEARING_OVERRIDE_TRUE: + return true; + case TEARING_WINDOW_HINT: + return view->tearing_hint == + WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; + } + return false; +} + +static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, + int x, int y, void *data) { + struct timespec *when = data; + wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); +} + +void view_send_frame_done(struct sway_view *view) { + struct timespec when; + clock_gettime(CLOCK_MONOTONIC, &when); + + struct wlr_scene_node *node; + wl_list_for_each(node, &view->content_tree->children, link) { + wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); + } +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 98a177fa..f8709a4c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -1,18 +1,16 @@ -#define _POSIX_C_SOURCE 200809 #include #include #include #include #include #include -#include #include "stringop.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" #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/node.h" @@ -73,6 +71,18 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); + + bool failed = false; + ws->layers.tiling = alloc_scene_tree(root->staging, &failed); + ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed); + + if (failed) { + wlr_scene_node_destroy(&ws->layers.tiling->node); + wlr_scene_node_destroy(&ws->layers.fullscreen->node); + free(ws); + return NULL; + } + ws->name = strdup(name); ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -133,6 +143,11 @@ void workspace_destroy(struct sway_workspace *workspace) { return; } + scene_node_disown_children(workspace->layers.tiling); + scene_node_disown_children(workspace->layers.fullscreen); + wlr_scene_node_destroy(&workspace->layers.tiling->node); + wlr_scene_node_destroy(&workspace->layers.fullscreen->node); + free(workspace->name); free(workspace->representation); list_free_items_and_destroy(workspace->output_priority); @@ -671,66 +686,9 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { if (workspace->urgent != new_urgent) { workspace->urgent = new_urgent; ipc_event_workspace(NULL, workspace, "urgent"); - output_damage_whole(workspace->output); } } -struct blur_region_data { - struct sway_workspace *ws; - pixman_region32_t *blur_region; -}; - -static void find_blurred_region_iterator(struct sway_container *con, void *data) { - struct sway_view *view = con->view; - if (!view) { - return; - } - - struct blur_region_data *region_data = data; - struct sway_workspace *ws = region_data->ws; - pixman_region32_t *blur_region = region_data->blur_region; - - if (con->blur_enabled && !view->surface->opaque) { - struct wlr_box region = { - .x = floor(con->current.x) - ws->output->lx, - .y = floor(con->current.y) - ws->output->ly, - .width = con->current.width, - .height = con->current.height, - }; - scale_box(®ion, ws->output->wlr_output->scale); - pixman_region32_union_rect(blur_region, blur_region, - region.x, region.y, region.width, region.height); - } -} - -bool workspace_get_blur_info(struct sway_workspace *ws, pixman_region32_t *blur_region) { - if (!workspace_is_visible(ws) || !config_should_parameters_blur()) { - return false; - } - - // Each toplevel - struct blur_region_data data = { ws, blur_region }; - workspace_for_each_container(ws, find_blurred_region_iterator, &data); - - // Each Layer - struct sway_output *sway_output = ws->output; - size_t len = sizeof(sway_output->layers) / sizeof(sway_output->layers[0]); - for (size_t i = 0; i < len; ++i) { - struct sway_layer_surface *lsurface; - wl_list_for_each(lsurface, &sway_output->layers[i], link) { - if (lsurface->has_blur && !lsurface->layer_surface->surface->opaque - && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { - struct wlr_box geo = lsurface->geo; - scale_box(&geo, sway_output->wlr_output->scale); - pixman_region32_union_rect(blur_region, blur_region, - geo.x, geo.y, geo.width, geo.height); - } - } - } - - return pixman_region32_not_empty(blur_region); -} - void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data) { // Tiling @@ -750,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 3a035972..fd604874 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,4 +1,5 @@ #include +#include #include "sway/desktop/launcher.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -37,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); diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index f7f5f5ed..fa8c6279 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c @@ -23,6 +23,45 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data) { struct sway_xdg_decoration *deco = wl_container_of(listener, deco, request_mode); + set_xdg_decoration_mode(deco); +} + +void handle_xdg_decoration(struct wl_listener *listener, void *data) { + struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; + struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; + + struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); + if (deco == NULL) { + return; + } + + deco->view = &xdg_shell_view->view; + deco->view->xdg_decoration = deco; + deco->wlr_xdg_decoration = wlr_deco; + + wl_signal_add(&wlr_deco->events.destroy, &deco->destroy); + deco->destroy.notify = xdg_decoration_handle_destroy; + + wl_signal_add(&wlr_deco->events.request_mode, &deco->request_mode); + deco->request_mode.notify = xdg_decoration_handle_request_mode; + + wl_list_insert(&server.xdg_decorations, &deco->link); + + set_xdg_decoration_mode(deco); +} + +struct sway_xdg_decoration *xdg_decoration_from_surface( + struct wlr_surface *surface) { + struct sway_xdg_decoration *deco; + wl_list_for_each(deco, &server.xdg_decorations, link) { + if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { + return deco; + } + } + return NULL; +} + +void set_xdg_decoration_mode(struct sway_xdg_decoration *deco) { struct sway_view *view = deco->view; enum wlr_xdg_toplevel_decoration_v1_mode mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; @@ -47,41 +86,7 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, mode = client_mode; } - wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, - mode); -} - -void handle_xdg_decoration(struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; - struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; - - struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); - if (deco == NULL) { - return; + if (view->wlr_xdg_toplevel->base->initialized) { + wlr_xdg_toplevel_decoration_v1_set_mode(deco->wlr_xdg_decoration, mode); } - - deco->view = &xdg_shell_view->view; - deco->view->xdg_decoration = deco; - deco->wlr_xdg_decoration = wlr_deco; - - wl_signal_add(&wlr_deco->events.destroy, &deco->destroy); - deco->destroy.notify = xdg_decoration_handle_destroy; - - wl_signal_add(&wlr_deco->events.request_mode, &deco->request_mode); - deco->request_mode.notify = xdg_decoration_handle_request_mode; - - wl_list_insert(&server.xdg_decorations, &deco->link); - - xdg_decoration_handle_request_mode(&deco->request_mode, wlr_deco); -} - -struct sway_xdg_decoration *xdg_decoration_from_surface( - struct wlr_surface *surface) { - struct sway_xdg_decoration *deco; - wl_list_for_each(deco, &server.xdg_decorations, link) { - if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { - return deco; - } - } - return NULL; } diff --git a/swaybar/bar.c b/swaybar/bar.c index 021fc3bd..4d20f20f 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -509,7 +508,7 @@ void bar_run(struct swaybar *bar) { } #if HAVE_TRAY if (bar->tray) { - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } #endif while (bar->running) { diff --git a/swaybar/config.c b/swaybar/config.c index 5e828773..55bfcb72 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "swaybar/config.h" diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index ccd5a076..62c22d43 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/common/background-image.c b/swaybar/image.c similarity index 56% rename from common/background-image.c rename to swaybar/image.c index 994a0805..ed24b9f9 100644 --- a/common/background-image.c +++ b/swaybar/image.c @@ -1,29 +1,12 @@ #include -#include "background-image.h" -#include "cairo_util.h" +#include "config.h" #include "log.h" +#include "swaybar/image.h" + #if HAVE_GDK_PIXBUF #include #endif -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; - } - sway_log(SWAY_ERROR, "Unsupported background mode: %s", mode); - return BACKGROUND_MODE_INVALID; -} - #if HAVE_GDK_PIXBUF static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( const GdkPixbuf *gdkbuf) { @@ -121,7 +104,7 @@ static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( } #endif // HAVE_GDK_PIXBUF -cairo_surface_t *load_background_image(const char *path) { +cairo_surface_t *load_image(const char *path) { cairo_surface_t *image; #if HAVE_GDK_PIXBUF GError *err = NULL; @@ -151,70 +134,3 @@ cairo_surface_t *load_background_image(const char *path) { } 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); - - cairo_save(cairo); - 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); - cairo_restore(cairo); -} diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 33ae6544..71c9a4c5 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809 #include #include #include @@ -519,8 +518,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, #if HAVE_TRAY if (oldcfg->tray_hidden && !newcfg->tray_hidden) { bar->tray = create_tray(bar); - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, - bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } else if (bar->tray && newcfg->tray_hidden) { loop_remove_fd(bar->eventloop, bar->tray->fd); destroy_tray(bar->tray); diff --git a/swaybar/main.c b/swaybar/main.c index d1e7d4a6..54fb8d3e 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaybar/meson.build b/swaybar/meson.build index e5f1811e..34bbdeea 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -26,6 +26,7 @@ executable( 'bar.c', 'config.c', 'i3bar.c', + 'image.c', 'input.c', 'ipc.c', 'main.c', diff --git a/swaybar/render.c b/swaybar/render.c index 1113ca44..879a4e42 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 2e9bb7f1..e542e606 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index eea2caa5..79b54606 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index b513dca5..659edd86 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 1f18b8bb..ca6c03ad 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -7,12 +6,12 @@ #include #include "swaybar/bar.h" #include "swaybar/config.h" +#include "swaybar/image.h" #include "swaybar/input.h" #include "swaybar/tray/host.h" #include "swaybar/tray/icon.h" #include "swaybar/tray/item.h" #include "swaybar/tray/tray.h" -#include "background-image.h" #include "cairo_util.h" #include "list.h" #include "log.h" @@ -431,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme, list_free(icon_search_paths); if (icon_path) { cairo_surface_destroy(sni->icon); - sni->icon = load_background_image(icon_path); + sni->icon = load_image(icon_path); free(icon_path); return; } diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index b0545f4a..a4f382bf 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -90,9 +91,16 @@ void destroy_tray(struct swaybar_tray *tray) { } void tray_in(int fd, short mask, void *data) { - sd_bus *bus = data; + struct swaybar *bar = data; int ret; - while ((ret = sd_bus_process(bus, NULL)) > 0) { + + if (mask & (POLLHUP | POLLERR)) { + sway_log(SWAY_ERROR, "D-Bus connection closed unexpectedly"); + bar->running = false; + return; + } + + while ((ret = sd_bus_process(bar->tray->bus, NULL)) > 0) { // This space intentionally left blank } if (ret < 0) { diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 2458a8c2..54f000bd 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include @@ -39,6 +38,8 @@ static int handle_lost_service(sd_bus_message *msg, list_del(watcher->items, idx--); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemUnregistered", "s", id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); free(id); if (using_standard_protocol(watcher)) { break; @@ -51,6 +52,10 @@ static int handle_lost_service(sd_bus_message *msg, sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); list_del(watcher->hosts, idx); + if (watcher->hosts->length == 0) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } } } @@ -83,6 +88,8 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->items, cmp_id, id) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id); list_add(watcher->items, id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemRegistered", "s", id); } else { @@ -105,6 +112,10 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); list_add(watcher->hosts, strdup(service)); + if (watcher->hosts->length == 1) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierHostRegistered", ""); } else { diff --git a/swaymsg/main.c b/swaymsg/main.c index f408307e..e98c9d7f 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include @@ -194,7 +193,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, - *transform, *max_render_time, *adaptive_sync_status; + *transform, *max_render_time, *adaptive_sync_status, *allow_tearing; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); @@ -204,6 +203,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status); + json_object_object_get_ex(o, "allow_tearing", &allow_tearing); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "y", &y); @@ -257,6 +257,9 @@ static void pretty_print_output(json_object *o) { printf(" Adaptive sync: %s\n", json_object_get_string(adaptive_sync_status)); + + printf(" Allow tearing: %s\n", + json_object_get_boolean(allow_tearing) ? "yes" : "no"); } else { printf( "Output %s '%s %s %s' (disabled)\n", @@ -297,12 +300,9 @@ static void pretty_print_output(json_object *o) { } static void pretty_print_version(json_object *v) { - json_object *swayfx_ver; - json_object *sway_ver; - json_object_object_get_ex(v, "human_readable", &swayfx_ver); - json_object_object_get_ex(v, "sway_original_version", &sway_ver); - printf("swayfx version %s (based on sway %s)\n", - json_object_get_string(swayfx_ver), json_object_get_string(sway_ver)); + json_object *ver; + json_object_object_get_ex(v, "human_readable", &ver); + printf("sway version %s\n", json_object_get_string(ver)); } static void pretty_print_config(json_object *c) { diff --git a/swaynag/config.c b/swaynag/config.c index b355b153..a278a99d 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaynag/main.c b/swaynag/main.c index 20390207..634bddbf 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include "log.h" diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 6ea739e3..50eea148 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include diff --git a/swaynag/types.c b/swaynag/types.c index 409cc668..821e5b21 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -1,4 +1,3 @@ -#define _POSIX_C_SOURCE 200809L #include #include #include