From cf3ef50a3fb8bf6d8b63d84d15cfdf0ae72daedb Mon Sep 17 00:00:00 2001 From: Babymusk <95881676+babymu5k@users.noreply.github.com> Date: Thu, 15 Feb 2024 02:27:51 +0000 Subject: [PATCH 01/18] Added guide for Debian users (#268) * Create INSTALL-deb * Rename INSTALL-deb to INSTALL-deb.md * Update README.md to include debian * Added a guide * Update README.md * Update INSTALL-deb.md * Version change --- INSTALL-deb.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 3 +++ 2 files changed, 52 insertions(+) create mode 100644 INSTALL-deb.md diff --git a/INSTALL-deb.md b/INSTALL-deb.md new file mode 100644 index 00000000..63e261cb --- /dev/null +++ b/INSTALL-deb.md @@ -0,0 +1,49 @@ +## A guide to installing swayfx on Debian +( You may have to adapt this guide to install on other distros ) + +### First install all required dependencies and download the source code + +```bash +sudo apt install meson wayland-protocols wayland libpcre2-dev libjson-c-dev libpango-1.0-0 libcairo2-dev wget +``` + +### Next setup the build enviroment +```bash +mkdir ~/build +cd ~/build # Or whatever you have named it +# Downloading Wlroots +wget https://gitlab.freedesktop.org/wlroots/wlroots/-/archive/0.16.2/wlroots-0.16.2.tar.gz +tar -xf wlroots-0.16.2.tar.gz +rm wlroots-0.16.2.tar.gz + +# Downloaing Swayfx +wget https://github.com/WillPower3309/swayfx/archive/refs/tags/0.3.2.tar.gz +tar -xf 0.3.2.tar.gz +rm 0.3.2.tar.gz +``` +swayfx and wlroots should now be located in the `build` directory. +``` +. +├── swayfx-0.3.2 +├── wlroots-0.16.2 +``` +___ +### Compiling +You MUST compile wlroots first. +```bash +cd wlroots-0.16.2 +meson setup build/ +ninja -C build/ +``` + +Now to compile swayfx. +```bash +cd swayfx-0.3.2 +meson build/ +ninja -C build/ +sudo ninja -C build/ install +``` +Reboot and then add the desired effects in your ~/.config/sway/config file
+e.g. `blur enable|disable` + ++ Guide created with ♥️ by babymusk diff --git a/README.md b/README.md index 01a777da..e5b467ef 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ You can also bring up a development shell and follow the build instructions belo ``` nix develop ``` +### Debian +Check [INSTALL-deb.md](/INSTALL-deb.md) + ### Manual Steps From 271af3d4748c7c0c4f45c3c250c33fb7b3399ed5 Mon Sep 17 00:00:00 2001 From: Rolf Vidar Mazunki Hoksaas <32819373+mazunki@users.noreply.github.com> Date: Thu, 15 Feb 2024 03:29:04 +0100 Subject: [PATCH 02/18] enhancement: update version printing format (#254) * enhancement: update version printing format as mentioned in #253, version numbers can be confusing if you don't know or realize you're running SwayFX instead of standard sway. to fix this issue, I've updated the way we display versions, replacing "sway version X" to "swayfx version X", suffixed by the sway upstream version which swayfx is based upon. while not immediately displaying we come from swayfx in swaymsg/swaynag/swaybar, we do display we're based off a specific upstream sway version. note: this requires updating `meson.build` (currently `meson.build:181`) each time we rebase into a new sway version. Closes: Issue #253 Signed-off-by: Mazunki Hoksaas * reduce linelength --------- Signed-off-by: Mazunki Hoksaas --- meson.build | 1 + sway/ipc-json.c | 1 + sway/main.c | 7 ++++--- swaybar/main.c | 2 +- swaymsg/main.c | 11 +++++++---- swaynag/config.c | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 74637d84..8d43af84 100644 --- a/meson.build +++ b/meson.build @@ -178,6 +178,7 @@ if git.found() endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') +add_project_arguments('-DSWAY_ORIGINAL_VERSION="1.8.1"', language: 'c') # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 2ae09457..eea4e186 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -209,6 +209,7 @@ json_object *ipc_json_get_version(void) { sscanf(SWAY_VERSION, "%d.%d.%d", &major, &minor, &patch); json_object_object_add(version, "human_readable", json_object_new_string(SWAY_VERSION)); + json_object_object_add(version, "sway_original_version", json_object_new_string(SWAY_ORIGINAL_VERSION)); json_object_object_add(version, "variant", json_object_new_string("sway")); json_object_object_add(version, "major", json_object_new_int(major)); json_object_object_add(version, "minor", json_object_new_int(minor)); diff --git a/sway/main.c b/sway/main.c index 85bc2f1c..681a24e9 100644 --- a/sway/main.c +++ b/sway/main.c @@ -288,7 +288,7 @@ int main(int argc, char **argv) { allow_unsupported_gpu = true; break; case 'v': // version - printf("sway version " SWAY_VERSION "\n"); + printf("swayfx version " SWAY_VERSION " (based on sway " SWAY_ORIGINAL_VERSION ")\n"); exit(EXIT_SUCCESS); break; case 'V': // verbose @@ -335,7 +335,7 @@ int main(int argc, char **argv) { wlr_log_init(WLR_ERROR, handle_wlr_log); } - sway_log(SWAY_INFO, "Sway version " SWAY_VERSION); + sway_log(SWAY_INFO, "swayfx version " SWAY_VERSION " (based on sway version " SWAY_ORIGINAL_VERSION ")"); sway_log(SWAY_INFO, "wlroots version " WLR_VERSION_STR); log_kernel(); log_distro(); @@ -373,7 +373,8 @@ int main(int argc, char **argv) { // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); - sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); + sway_log(SWAY_INFO, "Starting swayfx version " SWAY_VERSION + " (based on sway version " SWAY_ORIGINAL_VERSION ")"); root = root_create(); diff --git a/swaybar/main.c b/swaybar/main.c index a44c1e63..d1e7d4a6 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -55,7 +55,7 @@ int main(int argc, char **argv) { swaybar.id = strdup(optarg); break; case 'v': - printf("swaybar version " SWAY_VERSION "\n"); + printf("swaybar version " SWAY_VERSION " (based on sway " SWAY_ORIGINAL_VERSION ")\n"); exit(EXIT_SUCCESS); break; case 'd': // Debug diff --git a/swaymsg/main.c b/swaymsg/main.c index c0b5809e..237a4a71 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -294,9 +294,12 @@ static void pretty_print_output(json_object *o) { } static void pretty_print_version(json_object *v) { - json_object *ver; - json_object_object_get_ex(v, "human_readable", &ver); - printf("sway version %s\n", json_object_get_string(ver)); + 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)); } static void pretty_print_config(json_object *c) { @@ -470,7 +473,7 @@ int main(int argc, char **argv) { cmdtype = strdup(optarg); break; case 'v': - printf("swaymsg version " SWAY_VERSION "\n"); + printf("swaymsg version " SWAY_VERSION " (based on sway " SWAY_ORIGINAL_VERSION ")\n"); exit(EXIT_SUCCESS); break; default: diff --git a/swaynag/config.c b/swaynag/config.c index a0bf3197..db86c1b8 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -278,7 +278,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, } break; case 'v': // Version - printf("swaynag version " SWAY_VERSION "\n"); + printf("swaynag version " SWAY_VERSION " (based on sway " SWAY_ORIGINAL_VERSION ")\n"); return -1; case TO_COLOR_BACKGROUND: // Background color if (type && !parse_color(optarg, &type->background)) { From d1482ad992001facc89c4bd93ee7fb6995bc134c Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 15 Feb 2024 09:47:41 +0100 Subject: [PATCH 03/18] Added sway-portal.conf to COPR (#234) --- build-scripts/swayfx.rpkg.spec | 20 ++++++++++---------- sway-portals.conf | 6 ++++++ 2 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 sway-portals.conf diff --git a/build-scripts/swayfx.rpkg.spec b/build-scripts/swayfx.rpkg.spec index 684a22a8..6ad2534c 100644 --- a/build-scripts/swayfx.rpkg.spec +++ b/build-scripts/swayfx.rpkg.spec @@ -8,12 +8,13 @@ Name: {{{ git_dir_name }}} Version: %{Tag} -Release: 1%{?dist} +Release: 2%{?dist} Summary: SwayFX: Sway, but with eye candy! License: MIT URL: https://github.com/WillPower3309/swayfx VCS: {{{ git_dir_vcs }}} Source: {{{ git_dir_pack }}} +Source101: sway-portals.conf BuildRequires: gcc-c++ BuildRequires: gnupg2 @@ -113,6 +114,9 @@ sed -i "s|^output \* bg .*|output * bg /usr/share/backgrounds/default.png fill|" # Create directory for extra config snippets install -d -m755 -pv %{buildroot}%{_sysconfdir}/sway/config.d +# Install portals.conf for xdg-desktop-portal +install -D -m644 -pv %{SOURCE101} %{buildroot}%{_datadir}/xdg-desktop-portal/sway-portals.conf + # install python scripts from contrib install -D -m644 -pv -t %{buildroot}%{_datadir}/sway/contrib contrib/*.py @@ -135,15 +139,11 @@ install -D -m755 -pv contrib/grimshot %{buildroot}%{_bindir}/grimshot %{_bindir}/swaynag %{_datadir}/sway %{_datadir}/wayland-sessions/sway.desktop -%dir %{_datadir}/zsh -%dir %{_datadir}/zsh/site-functions -%{_datadir}/zsh/site-functions/_sway* -%dir %{_datadir}/bash-completion -%dir %{_datadir}/bash-completion/completions -%{_datadir}/bash-completion/completions/sway* -%dir %{_datadir}/fish -%dir %{_datadir}/fish/vendor_completions.d -%{_datadir}/fish/vendor_completions.d/sway* +%dir %{_datadir}/xdg-desktop-portal +%{_datadir}/xdg-desktop-portal/sway-portals.conf +%{bash_completions_dir}/sway* +%{fish_completions_dir}/sway*.fish +%{zsh_completions_dir}/_sway* %files wallpapers %license assets/LICENSE diff --git a/sway-portals.conf b/sway-portals.conf new file mode 100644 index 00000000..aa046f63 --- /dev/null +++ b/sway-portals.conf @@ -0,0 +1,6 @@ +[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 From 2bd366f3372d6f94f6633e62b7f7b06fcf316943 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 15 Feb 2024 09:48:31 +0100 Subject: [PATCH 04/18] Add blur to tiling indicator (#252) --- include/sway/desktop/fx_renderer/fx_renderer.h | 2 ++ include/sway/output.h | 11 +++++++++++ sway/desktop/render.c | 10 ++-------- sway/input/seatop_move_tiling.c | 10 ++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h index 924d9951..7643caaf 100644 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ b/include/sway/desktop/fx_renderer/fx_renderer.h @@ -180,6 +180,8 @@ struct fx_renderer { } shaders; }; +struct decoration_data get_undecorated_decoration_data(); + struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *output); void fx_renderer_fini(struct fx_renderer *renderer); diff --git a/include/sway/output.h b/include/sway/output.h index 3215c853..bd65237e 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -19,6 +19,12 @@ struct render_data { struct decoration_data deco_data; }; +struct blur_stencil_data { + struct fx_texture *stencil_texture; + const struct wlr_fbox *stencil_src_box; + float *stencil_matrix; +}; + struct sway_output_state { list_t *workspaces; struct sway_workspace *active_workspace; @@ -184,6 +190,11 @@ void render_rounded_rect(struct sway_output *output, float color[static 4], int corner_radius, enum corner_location corner_location); +void render_blur(bool optimized, struct sway_output *output, + pixman_region32_t *output_damage, const struct wlr_box *dst_box, + pixman_region32_t *opaque_region, struct decoration_data *deco_data, + struct blur_stencil_data *stencil_data); + void premultiply_alpha(float color[4], float opacity); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index e1deb33b..429924c2 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -295,12 +295,6 @@ struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct return current_buffer; } -struct blur_stencil_data { - struct fx_texture *stencil_texture; - const struct wlr_fbox *stencil_src_box; - float *stencil_matrix; -}; - void render_blur(bool optimized, struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *dst_box, pixman_region32_t *opaque_region, struct decoration_data *deco_data, @@ -336,7 +330,7 @@ void render_blur(bool optimized, struct sway_output *output, } // Get a stencil of the window ignoring transparent regions - if (deco_data->discard_transparent) { + if (deco_data->discard_transparent && stencil_data) { fx_renderer_scissor(NULL); fx_renderer_stencil_mask_init(); @@ -358,7 +352,7 @@ void render_blur(bool optimized, struct sway_output *output, render_texture(wlr_output, &damage, &buffer->texture, NULL, dst_box, matrix, blur_deco_data); // Finish stenciling - if (deco_data->discard_transparent) { + if (deco_data->discard_transparent && stencil_data) { fx_renderer_stencil_mask_fini(); } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 87e887ca..4e4db18c 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -45,6 +45,16 @@ static void handle_render(struct sway_seat *seat, struct wlr_box box; memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); scale_box(&box, output->wlr_output->scale); + + // Render blur + pixman_region32_t opaque_region; + pixman_region32_init(&opaque_region); + struct decoration_data deco_data = get_undecorated_decoration_data(); + deco_data.blur = e->con->blur_enabled; + deco_data.corner_radius = e->con->corner_radius; + render_blur(false, output, damage, &box, &opaque_region, &deco_data, NULL); + pixman_region32_fini(&opaque_region); + render_rounded_rect(output, damage, &box, color, e->con->corner_radius * output->wlr_output->scale, ALL); } From 8afe66ab31038165916a96c88b4a1f9a48f0dd1d Mon Sep 17 00:00:00 2001 From: ozwaldorf Date: Wed, 10 Apr 2024 00:58:34 -0400 Subject: [PATCH 05/18] Overhaul nix flake (#279) --- flake.lock | 23 ++---------- flake.nix | 100 +++++++++++++++++++++++++---------------------------- 2 files changed, 50 insertions(+), 73 deletions(-) diff --git a/flake.lock b/flake.lock index 2ecb64b1..e99f60b5 100644 --- a/flake.lock +++ b/flake.lock @@ -1,28 +1,12 @@ { "nodes": { - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1684585791, - "narHash": "sha256-lYPboblKrchmbkGMoAcAivomiOscZCjtGxxTSCY51SM=", + "lastModified": 1712192574, + "narHash": "sha256-LbbVOliJKTF4Zl2b9salumvdMXuQBr2kuKP5+ZwbYq4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "eea79d584eff53bf7a76aeb63f8845da6d386129", + "rev": "f480f9d09e4b4cf87ee6151eba068197125714de", "type": "github" }, "original": { @@ -34,7 +18,6 @@ }, "root": { "inputs": { - "flake-compat": "flake-compat", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index aec8c24a..d71313cf 100644 --- a/flake.nix +++ b/flake.nix @@ -1,73 +1,67 @@ { - description = "swaywm development environment"; - - inputs = { - flake-compat = { - url = "github:edolstra/flake-compat"; - flake = false; - }; - - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - }; - - outputs = { self, nixpkgs, flake-compat, ... }: + description = "Swayfx development environment"; + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + outputs = + { self, nixpkgs, ... }: let - pkgsFor = system: - import nixpkgs { - inherit system; - overlays = [ ]; - }; - - targetSystems = [ "aarch64-linux" "x86_64-linux" ]; + pkgsFor = system: import nixpkgs { inherit system; }; + targetSystems = [ + "aarch64-linux" + "x86_64-linux" + ]; + mkPackage = pkgs: { + swayfx-unwrapped = + (pkgs.swayfx-unwrapped.override { + # When the sway 1.9 rebase is finished, this will need to be overridden. + # wlroots_0_16 = pkgs.wlroots_0_16; + }).overrideAttrs + (old: { + version = "0.3.2-git"; + src = pkgs.lib.cleanSource ./.; + }); + }; in { - overlays.default = final: prev: { - swayfx-unwrapped = prev.sway-unwrapped.overrideAttrs (old: { - src = builtins.path { path = prev.lib.cleanSource ./.; }; - patches = - let - removePatches = [ - "LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM.patch" - ]; - in - builtins.filter - (patch: !builtins.elem (patch.name or null) removePatches) - (old.patches or [ ]); - }); + overlays = rec { + default = override; + # Override onto the input nixpkgs + override = _: prev: mkPackage prev; + # Insert using the locked nixpkgs + insert = _: prev: mkPackage (pkgsFor prev.system); }; - packages = nixpkgs.lib.genAttrs targetSystems (system: - let pkgs = pkgsFor system; - in (self.overlays.default pkgs pkgs) // { - default = self.packages.${system}.swayfx-unwrapped; - }); + packages = nixpkgs.lib.genAttrs targetSystems ( + system: (mkPackage (pkgsFor system) // { default = self.packages.${system}.swayfx-unwrapped; }) + ); - devShells = nixpkgs.lib.genAttrs targetSystems (system: + devShells = nixpkgs.lib.genAttrs targetSystems ( + system: let pkgs = pkgsFor system; in { default = pkgs.mkShell { name = "swayfx-shell"; - depsBuildBuild = with pkgs; [ pkg-config ]; - inputsFrom = [ self.packages.${system}.swayfx-unwrapped pkgs.wlroots_0_16 ]; - + inputsFrom = [ + self.packages.${system}.swayfx-unwrapped + pkgs.wlroots_0_16 + ]; nativeBuildInputs = with pkgs; [ cmake - meson - ninja - pkg-config wayland-scanner - scdoc - hwdata # for wlroots ]; - - shellHook = with pkgs; ''( - mkdir -p "$PWD/subprojects" - cd "$PWD/subprojects" - cp -R --no-preserve=mode,ownership ${wlroots_0_16.src} wlroots - )''; + # Copy the nix version of wlroots into the project + shellHook = with pkgs; '' + ( + mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects" + cp -R --no-preserve=mode,ownership ${wlroots_0_16.src} wlroots + )''; }; - }); + } + ); + + formatter = nixpkgs.lib.genAttrs targetSystems (system: { + default = (pkgsFor system).nixfmt-rfc-style; + }); }; } From 167a4ea0aec93d51e4c606ffdbe3a195d12da6e3 Mon Sep 17 00:00:00 2001 From: sntx <48636286+Sntx626@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:55:22 +0200 Subject: [PATCH 06/18] fix: Changes outputs, `nix flake show` now completes (#280) Removed the `default` attribute from `outputs.formatter.`, instead parse the formatter derivation directly. This fixes the issue that `nix flake show` doesn't complete. Co-authored-by: sntx --- flake.nix | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index d71313cf..7af8fe82 100644 --- a/flake.nix +++ b/flake.nix @@ -60,8 +60,6 @@ } ); - formatter = nixpkgs.lib.genAttrs targetSystems (system: { - default = (pkgsFor system).nixfmt-rfc-style; - }); + formatter = nixpkgs.lib.genAttrs targetSystems (system: (pkgsFor system).nixfmt-rfc-style); }; } From 477686adb0cf21f2b7be40c7017d17222e0522d0 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Sun, 14 Apr 2024 18:52:22 -0400 Subject: [PATCH 07/18] added hwdata wlroots dependency --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 7af8fe82..f63e587c 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,7 @@ nativeBuildInputs = with pkgs; [ cmake wayland-scanner + hwdata # for wlroots ]; # Copy the nix version of wlroots into the project shellHook = with pkgs; '' From a5e79676c4bd22fc5902182acf0667907202a465 Mon Sep 17 00:00:00 2001 From: ozwaldorf Date: Mon, 15 Apr 2024 01:32:43 -0400 Subject: [PATCH 08/18] flake: switch default overlay to insert for wider compatibility (#282) Advanced users can opt into using the override flake for layering on top of the flake, given they are on a compatible nixpkgs version and know what they are doing --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f63e587c..bd3edcea 100644 --- a/flake.nix +++ b/flake.nix @@ -23,7 +23,7 @@ in { overlays = rec { - default = override; + default = insert; # Override onto the input nixpkgs override = _: prev: mkPackage prev; # Insert using the locked nixpkgs From fb86ed6b0588dfdebfb66ce875bc63cfa0a897f6 Mon Sep 17 00:00:00 2001 From: Reza Jelveh Date: Mon, 15 Apr 2024 13:39:41 +0800 Subject: [PATCH 09/18] feat: 1.9 merge (#277) Co-authored-by: William McKinnon Co-authored-by: Erik Reider <35975961+ErikReider@users.noreply.github.com> --- .gitignore | 2 +- README.md | 3 +- common/gesture.c | 31 +- common/pango.c | 20 +- common/stringop.c | 33 + completions/meson.build | 57 + config.in | 4 +- contrib/autoname-workspaces.py | 124 -- contrib/grimshot | 168 -- contrib/grimshot.1 | 109 -- contrib/grimshot.1.scd | 80 - contrib/inactive-windows-transparency.py | 69 - flake.nix | 4 +- include/pango.h | 5 +- include/stringop.h | 9 + include/sway/commands.h | 12 +- include/sway/config.h | 18 +- include/sway/criteria.h | 1 + .../sway/desktop/fx_renderer/fx_framebuffer.h | 27 - .../sway/desktop/fx_renderer/fx_renderer.h | 236 --- .../desktop/fx_renderer/fx_stencilbuffer.h | 18 - include/sway/desktop/fx_renderer/fx_texture.h | 22 - include/sway/desktop/fx_renderer/matrix.h | 9 - include/sway/desktop/idle_inhibit_v1.h | 12 +- include/sway/desktop/launcher.h | 12 +- include/sway/input/cursor.h | 9 +- include/sway/input/input-manager.h | 3 +- include/sway/input/libinput.h | 3 + include/sway/input/seat.h | 42 +- include/sway/input/text_input.h | 3 +- include/sway/ipc-server.h | 1 + include/sway/layers.h | 4 + include/sway/output.h | 66 +- include/sway/server.h | 22 +- include/sway/surface.h | 6 + include/sway/swaynag.h | 3 +- include/sway/tree/container.h | 8 + include/sway/tree/node.h | 1 + include/sway/tree/view.h | 13 +- include/sway/tree/workspace.h | 1 + include/swaybar/bar.h | 2 + include/swaybar/tray/item.h | 1 + include/swaynag/swaynag.h | 6 +- include/swaynag/types.h | 5 +- meson.build | 126 +- meson_options.txt | 2 +- protocols/meson.build | 4 +- sway/commands.c | 26 +- sway/commands/assign.c | 2 +- sway/commands/bar.c | 4 +- sway/commands/bar/bind.c | 2 +- sway/commands/bar/tray_bind.c | 2 +- sway/commands/bind.c | 13 +- sway/commands/blur.c | 9 +- sway/commands/blur_brightness.c | 9 +- sway/commands/blur_contrast.c | 9 +- sway/commands/blur_noise.c | 15 +- sway/commands/blur_passes.c | 9 +- sway/commands/blur_radius.c | 9 +- sway/commands/blur_saturation.c | 9 +- sway/commands/blur_xray.c | 9 +- sway/commands/exec_always.c | 3 +- sway/commands/floating_minmax_size.c | 6 +- sway/commands/font.c | 6 +- sway/commands/for_window.c | 2 +- sway/commands/hide_edge_borders.c | 4 +- sway/commands/inhibit_idle.c | 2 +- sway/commands/input.c | 2 + sway/commands/input/events.c | 10 +- sway/commands/input/map_from_region.c | 18 +- sway/commands/input/map_to_region.c | 2 +- sway/commands/input/rotation_angle.c | 29 + sway/commands/input/scroll_button.c | 2 +- sway/commands/input/scroll_button_lock.c | 26 + sway/commands/layout.c | 2 +- sway/commands/move.c | 36 +- sway/commands/no_focus.c | 2 +- sway/commands/output.c | 5 +- sway/commands/output/background.c | 3 + sway/commands/primary_selection.c | 23 + sway/commands/rename.c | 4 +- sway/commands/resize.c | 50 +- sway/commands/seat/cursor.c | 10 +- sway/commands/seat/idle.c | 6 + sway/commands/shadows.c | 2 - sway/commands/shadows_on_csd.c | 4 - sway/commands/split.c | 2 +- sway/commands/swap.c | 179 +- sway/commands/workspace.c | 8 +- sway/config.c | 25 +- sway/config/bar.c | 1 - sway/config/input.c | 8 + sway/config/output.c | 102 +- sway/config/seat.c | 1 - sway/criteria.c | 13 +- sway/desktop/fx_renderer/fx_framebuffer.c | 89 - sway/desktop/fx_renderer/fx_renderer.c | 961 ----------- sway/desktop/fx_renderer/fx_stencilbuffer.c | 21 - sway/desktop/fx_renderer/fx_texture.c | 37 - sway/desktop/fx_renderer/matrix.c | 70 - sway/desktop/fx_renderer/shaders/blur1.frag | 18 - sway/desktop/fx_renderer/shaders/blur2.frag | 22 - .../fx_renderer/shaders/blur_effects.frag | 54 - .../fx_renderer/shaders/box_shadow.frag | 74 - sway/desktop/fx_renderer/shaders/common.vert | 12 - sway/desktop/fx_renderer/shaders/corner.frag | 36 - sway/desktop/fx_renderer/shaders/embed.sh | 11 - sway/desktop/fx_renderer/shaders/meson.build | 27 - sway/desktop/fx_renderer/shaders/quad.frag | 7 - .../fx_renderer/shaders/quad_round.frag | 39 - .../fx_renderer/shaders/stencil_mask.frag | 17 - sway/desktop/fx_renderer/shaders/tex.frag | 67 - sway/desktop/idle_inhibit_v1.c | 46 +- sway/desktop/launcher.c | 93 +- sway/desktop/layer_shell.c | 78 +- sway/desktop/output.c | 335 ++-- sway/desktop/render.c | 1511 +++++++---------- sway/desktop/surface.c | 26 + sway/desktop/transaction.c | 2 +- sway/desktop/xdg_shell.c | 40 +- sway/desktop/xwayland.c | 116 +- sway/input/cursor.c | 185 +- sway/input/input-manager.c | 60 +- sway/input/keyboard.c | 50 +- sway/input/libinput.c | 85 +- sway/input/seat.c | 167 +- sway/input/seatop_default.c | 135 +- sway/input/seatop_down.c | 131 +- sway/input/seatop_move_tiling.c | 13 +- sway/input/switch.c | 1 - sway/input/tablet.c | 14 +- sway/ipc-json.c | 46 +- sway/ipc-server.c | 18 +- sway/lock.c | 21 +- sway/meson.build | 19 +- sway/server.c | 139 +- sway/sway-input.5.scd | 36 +- sway/sway-ipc.7.scd | 20 + sway/sway.5.scd | 17 +- sway/swaynag.c | 14 +- sway/tree/arrange.c | 14 +- sway/tree/container.c | 296 +++- sway/tree/output.c | 3 +- sway/tree/root.c | 41 +- sway/tree/view.c | 65 +- sway/tree/workspace.c | 25 +- sway/xdg_activation_v1.c | 43 +- sway/xdg_decoration.c | 4 +- swaybar/bar.c | 21 +- swaybar/input.c | 27 +- swaybar/ipc.c | 9 +- swaybar/render.c | 31 +- swaybar/tray/host.c | 10 +- swaybar/tray/icon.c | 26 +- swaybar/tray/watcher.c | 11 +- swaymsg/main.c | 9 +- swaymsg/swaymsg.1.scd | 2 + swaynag/config.c | 10 +- swaynag/main.c | 25 +- swaynag/swaynag.c | 42 +- swaynag/types.c | 8 +- 161 files changed, 3161 insertions(+), 4682 deletions(-) create mode 100644 completions/meson.build delete mode 100755 contrib/autoname-workspaces.py delete mode 100755 contrib/grimshot delete mode 100644 contrib/grimshot.1 delete mode 100644 contrib/grimshot.1.scd delete mode 100755 contrib/inactive-windows-transparency.py delete mode 100644 include/sway/desktop/fx_renderer/fx_framebuffer.h delete mode 100644 include/sway/desktop/fx_renderer/fx_renderer.h delete mode 100644 include/sway/desktop/fx_renderer/fx_stencilbuffer.h delete mode 100644 include/sway/desktop/fx_renderer/fx_texture.h delete mode 100644 include/sway/desktop/fx_renderer/matrix.h create mode 100644 sway/commands/input/rotation_angle.c create mode 100644 sway/commands/input/scroll_button_lock.c create mode 100644 sway/commands/primary_selection.c delete mode 100644 sway/desktop/fx_renderer/fx_framebuffer.c delete mode 100644 sway/desktop/fx_renderer/fx_renderer.c delete mode 100644 sway/desktop/fx_renderer/fx_stencilbuffer.c delete mode 100644 sway/desktop/fx_renderer/fx_texture.c delete mode 100644 sway/desktop/fx_renderer/matrix.c delete mode 100644 sway/desktop/fx_renderer/shaders/blur1.frag delete mode 100644 sway/desktop/fx_renderer/shaders/blur2.frag delete mode 100644 sway/desktop/fx_renderer/shaders/blur_effects.frag delete mode 100644 sway/desktop/fx_renderer/shaders/box_shadow.frag delete mode 100644 sway/desktop/fx_renderer/shaders/common.vert delete mode 100644 sway/desktop/fx_renderer/shaders/corner.frag delete mode 100644 sway/desktop/fx_renderer/shaders/embed.sh delete mode 100644 sway/desktop/fx_renderer/shaders/meson.build delete mode 100644 sway/desktop/fx_renderer/shaders/quad.frag delete mode 100644 sway/desktop/fx_renderer/shaders/quad_round.frag delete mode 100644 sway/desktop/fx_renderer/shaders/stencil_mask.frag delete mode 100644 sway/desktop/fx_renderer/shaders/tex.frag diff --git a/.gitignore b/.gitignore index ba02e504..160e0867 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,11 @@ install_manifest.txt *.swp *.o *.a -.cache/ bin/ test/ build/ build-*/ +.cache/ !build-scripts !build-scripts/* .lvimrc diff --git a/README.md b/README.md index e5b467ef..f14e31aa 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ Install dependencies: * json-c * pango * cairo -* gdk-pixbuf2 (optional: system tray) +* gdk-pixbuf2 (optional: additional image formats for system tray) +* [swaybg] (optional: wallpaper) * [scdoc] (optional: man pages) \* * git (optional: version info) \* diff --git a/common/gesture.c b/common/gesture.c index 8c2efe99..58170443 100644 --- a/common/gesture.c +++ b/common/gesture.c @@ -12,23 +12,6 @@ const uint8_t GESTURE_FINGERS_ANY = 0; -// Helper to easily allocate and format string -static char *strformat(const char *format, ...) { - va_list args; - va_start(args, format); - int length = vsnprintf(NULL, 0, format, args) + 1; - va_end(args); - - char *result = malloc(length); - if (result) { - va_start(args, format); - vsnprintf(result, length, format, args); - va_end(args); - } - - return result; -} - char *gesture_parse(const char *input, struct gesture *output) { // Clear output in case of failure output->type = GESTURE_TYPE_NONE; @@ -38,7 +21,7 @@ char *gesture_parse(const char *input, struct gesture *output) { // Split input type, fingers and directions list_t *split = split_string(input, ":"); if (split->length < 1 || split->length > 3) { - return strformat( + return format_str( "expected [:][:direction], got %s", input); } @@ -51,8 +34,8 @@ char *gesture_parse(const char *input, struct gesture *output) { } else if (strcmp(split->items[0], "swipe") == 0) { output->type = GESTURE_TYPE_SWIPE; } else { - return strformat("expected hold|pinch|swipe, got %s", - split->items[0]); + return format_str("expected hold|pinch|swipe, got %s", + (const char *)split->items[0]); } // Parse optional arguments @@ -67,7 +50,7 @@ char *gesture_parse(const char *input, struct gesture *output) { next = split->length == 3 ? split->items[2] : NULL; } else if (split->length == 3) { // Fail here if argument can only be finger count - return strformat("expected 1-9, got %s", next); + return format_str("expected 1-9, got %s", next); } // If there is an argument left, try to parse as direction @@ -95,7 +78,7 @@ char *gesture_parse(const char *input, struct gesture *output) { } else if (strcmp(item, "counterclockwise") == 0) { output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; } else { - return strformat("expected direction, got %s", item); + return format_str("expected direction, got %s", item); } } list_free_items_and_destroy(directions); @@ -163,7 +146,7 @@ static char *gesture_directions_to_string(uint32_t directions) { if (!result) { result = strdup(name); } else { - char *new = strformat("%s+%s", result, name); + char *new = format_str("%s+%s", result, name); free(result); result = new; } @@ -179,7 +162,7 @@ static char *gesture_directions_to_string(uint32_t directions) { char *gesture_to_string(struct gesture *gesture) { char *directions = gesture_directions_to_string(gesture->directions); - char *result = strformat("%s:%u:%s", + char *result = format_str("%s:%u:%s", gesture_type_string(gesture->type), gesture->fingers, directions); free(directions); diff --git a/common/pango.c b/common/pango.c index e04bf80f..288569b3 100644 --- a/common/pango.c +++ b/common/pango.c @@ -84,18 +84,11 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *baseline, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); - // Add one since vsnprintf excludes null terminator. - int length = vsnprintf(NULL, 0, fmt, args) + 1; + char *buf = vformat_str(fmt, args); va_end(args); - - char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } - va_start(args, fmt); - vsnprintf(buf, length, fmt, args); - va_end(args); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); pango_cairo_update_layout(cairo, layout); @@ -104,6 +97,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; } g_object_unref(layout); + free(buf); } @@ -125,18 +119,11 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); - // Add one since vsnprintf excludes null terminator. - int length = vsnprintf(NULL, 0, fmt, args) + 1; + char *buf = vformat_str(fmt, args); va_end(args); - - char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } - va_start(args, fmt); - vsnprintf(buf, length, fmt, args); - va_end(args); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); cairo_font_options_t *fo = cairo_font_options_create(); @@ -146,5 +133,6 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, pango_cairo_update_layout(cairo, layout); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); + free(buf); } diff --git a/common/stringop.c b/common/stringop.c index 7fb3fe12..c503143a 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -328,3 +329,35 @@ bool expand_path(char **path) { wordfree(&p); return true; } + +char *vformat_str(const char *fmt, va_list args) { + char *str = NULL; + va_list args_copy; + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args); + if (len < 0) { + sway_log_errno(SWAY_ERROR, "vsnprintf(\"%s\") failed", fmt); + goto out; + } + + str = malloc(len + 1); + if (str == NULL) { + sway_log_errno(SWAY_ERROR, "malloc() failed"); + goto out; + } + + vsnprintf(str, len + 1, fmt, args_copy); + +out: + va_end(args_copy); + return str; +} + +char *format_str(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *str = vformat_str(fmt, args); + va_end(args); + return str; +} diff --git a/completions/meson.build b/completions/meson.build new file mode 100644 index 00000000..6bca9391 --- /dev/null +++ b/completions/meson.build @@ -0,0 +1,57 @@ +if get_option('zsh-completions') + zsh_files = files( + 'zsh/_sway', + 'zsh/_swaymsg', + ) + zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') + + install_data(zsh_files, install_dir: zsh_install_dir) +endif + +if get_option('bash-completions') + bash_comp = dependency('bash-completion', required: false) + + bash_files = files( + 'bash/sway', + 'bash/swaymsg', + ) + + if get_option('swaybar') + bash_files += files('bash/swaybar') + endif + + if bash_comp.found() + bash_install_dir = bash_comp.get_variable( + pkgconfig: 'completionsdir', + pkgconfig_define: ['datadir', datadir] + ) + else + bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') + endif + + install_data(bash_files, install_dir: bash_install_dir) +endif + +if get_option('fish-completions') + fish_comp = dependency('fish', required: false) + + fish_files = files( + 'fish/sway.fish', + 'fish/swaymsg.fish', + ) + + if get_option('swaynag') + fish_files += files('fish/swaynag.fish') + endif + + if fish_comp.found() + fish_install_dir = fish_comp.get_variable( + pkgconfig: 'completionsdir', + pkgconfig_define: ['datadir', datadir] + ) + else + fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') + endif + + install_data(fish_files, install_dir: fish_install_dir) +endif diff --git a/config.in b/config.in index 05b461db..d478178e 100644 --- a/config.in +++ b/config.in @@ -18,7 +18,7 @@ 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 | dmenu | xargs swaymsg exec -- +set $menu dmenu_path | wmenu | xargs swaymsg exec -- ### Appearance # window corner radius in px @@ -228,7 +228,7 @@ bar { # When the status_command prints a new line to stdout, swaybar updates. # The default just shows the current date and time. - status_command while date +'%Y-%m-%d %I:%M:%S %p'; do sleep 1; done + status_command while date +'%Y-%m-%d %X'; do sleep 1; done colors { statusline #ffffff diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py deleted file mode 100755 index 3ec39928..00000000 --- a/contrib/autoname-workspaces.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/python - -# This script requires i3ipc-python package (install it from a system package manager -# or pip). -# It adds icons to the workspace name for each open window. -# Set your keybindings like this: set $workspace1 workspace number 1 -# Add your icons to WINDOW_ICONS. -# Based on https://github.com/maximbaz/dotfiles/blob/master/bin/i3-autoname-workspaces - -import argparse -import i3ipc -import logging -import re -import signal -import sys - -WINDOW_ICONS = { - "firefox": "", -} - -DEFAULT_ICON = "󰀏" - - -def icon_for_window(window): - name = None - if window.app_id is not None and len(window.app_id) > 0: - name = window.app_id.lower() - elif window.window_class is not None and len(window.window_class) > 0: - name = window.window_class.lower() - - if name in WINDOW_ICONS: - return WINDOW_ICONS[name] - - logging.info("No icon available for window with name: %s" % str(name)) - return DEFAULT_ICON - -def rename_workspaces(ipc): - for workspace in ipc.get_tree().workspaces(): - name_parts = parse_workspace_name(workspace.name) - icon_tuple = () - for w in workspace: - if w.app_id is not None or w.window_class is not None: - icon = icon_for_window(w) - if not ARGUMENTS.duplicates and icon in icon_tuple: - continue - icon_tuple += (icon,) - name_parts["icons"] = " ".join(icon_tuple) + " " - new_name = construct_workspace_name(name_parts) - ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) - - -def undo_window_renaming(ipc): - for workspace in ipc.get_tree().workspaces(): - name_parts = parse_workspace_name(workspace.name) - name_parts["icons"] = None - new_name = construct_workspace_name(name_parts) - ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) - ipc.main_quit() - sys.exit(0) - - -def parse_workspace_name(name): - return re.match( - "(?P[0-9]+):?(?P\w+)? ?(?P.+)?", name - ).groupdict() - - -def construct_workspace_name(parts): - new_name = str(parts["num"]) - if parts["shortname"] or parts["icons"]: - new_name += ":" - - if parts["shortname"]: - new_name += parts["shortname"] - - if parts["icons"]: - new_name += " " + parts["icons"] - - return new_name - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="This script automatically changes the workspace name in sway depending on your open applications." - ) - parser.add_argument( - "--duplicates", - "-d", - action="store_true", - help="Set it when you want an icon for each instance of the same application per workspace.", - ) - parser.add_argument( - "--logfile", - "-l", - type=str, - default="/tmp/sway-autoname-workspaces.log", - help="Path for the logfile.", - ) - args = parser.parse_args() - global ARGUMENTS - ARGUMENTS = args - - logging.basicConfig( - level=logging.INFO, - filename=ARGUMENTS.logfile, - filemode="w", - format="%(message)s", - ) - - ipc = i3ipc.Connection() - - for sig in [signal.SIGINT, signal.SIGTERM]: - signal.signal(sig, lambda signal, frame: undo_window_renaming(ipc)) - - def window_event_handler(ipc, e): - if e.change in ["new", "close", "move"]: - rename_workspaces(ipc) - - ipc.on("window", window_event_handler) - - rename_workspaces(ipc) - - ipc.main() - diff --git a/contrib/grimshot b/contrib/grimshot deleted file mode 100755 index 1ec19def..00000000 --- a/contrib/grimshot +++ /dev/null @@ -1,168 +0,0 @@ -#!/bin/sh - -## Grimshot: a helper for screenshots within sway -## Requirements: -## - `grim`: screenshot utility for wayland -## - `slurp`: to select an area -## - `swaymsg`: to read properties of current window -## - `wl-copy`: clipboard utility -## - `jq`: json utility to parse swaymsg output -## - `notify-send`: to show notifications -## Those are needed to be installed, if unsure, run `grimshot check` -## -## See `man 1 grimshot` or `grimshot usage` for further details. - -getTargetDirectory() { - test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" && \ - . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" - - echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}" -} - -NOTIFY=no -CURSOR= - -while [ $# -gt 0 ]; do - key="$1" - - case $key in - -n|--notify) - NOTIFY=yes - shift # past argument - ;; - -c|--cursor) - CURSOR=yes - shift # past argument - ;; - *) # unknown option - break # done with parsing --flags - ;; - esac -done - -ACTION=${1:-usage} -SUBJECT=${2:-screen} -FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} - -if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then - echo "Usage:" - echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]" - echo " grimshot check" - echo " grimshot usage" - echo "" - echo "Commands:" - echo " copy: Copy the screenshot data into the clipboard." - echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT." - echo " check: Verify if required tools are installed and exit." - echo " usage: Show this message and exit." - echo "" - echo "Targets:" - echo " active: Currently active window." - echo " screen: All visible outputs." - echo " output: Currently active output." - echo " area: Manually select a region." - echo " window: Manually select a window." - exit -fi - -notify() { - notify-send -t 3000 -a grimshot "$@" -} -notifyOk() { - [ "$NOTIFY" = "no" ] && return - - TITLE=${2:-"Screenshot"} - MESSAGE=${1:-"OK"} - notify "$TITLE" "$MESSAGE" -} -notifyError() { - if [ $NOTIFY = "yes" ]; then - TITLE=${2:-"Screenshot"} - MESSAGE=${1:-"Error taking screenshot with grim"} - notify -u critical "$TITLE" "$MESSAGE" - else - echo "$1" - fi -} - -die() { - MSG=${1:-Bye} - notifyError "Error: $MSG" - exit 2 -} - -check() { - COMMAND=$1 - if command -v "$COMMAND" > /dev/null 2>&1; then - RESULT="OK" - else - RESULT="NOT FOUND" - fi - echo " $COMMAND: $RESULT" -} - -takeScreenshot() { - FILE=$1 - GEOM=$2 - OUTPUT=$3 - if [ -n "$OUTPUT" ]; then - grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" - elif [ -z "$GEOM" ]; then - grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim" - else - grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim" - fi -} - -if [ "$ACTION" = "check" ] ; then - echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..." - check grim - check slurp - check swaymsg - check wl-copy - check jq - check notify-send - exit -elif [ "$SUBJECT" = "area" ] ; then - GEOM=$(slurp -d) - # Check if user exited slurp without selecting the area - if [ -z "$GEOM" ]; then - exit 1 - fi - WHAT="Area" -elif [ "$SUBJECT" = "active" ] ; then - FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)') - GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"') - APP_ID=$(echo "$FOCUSED" | jq -r '.app_id') - WHAT="$APP_ID window" -elif [ "$SUBJECT" = "screen" ] ; then - GEOM="" - WHAT="Screen" -elif [ "$SUBJECT" = "output" ] ; then - GEOM="" - OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name') - WHAT="$OUTPUT" -elif [ "$SUBJECT" = "window" ] ; then - GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) - # Check if user exited slurp without selecting the area - if [ -z "$GEOM" ]; then - exit 1 - fi - WHAT="Window" -else - die "Unknown subject to take a screen shot from" "$SUBJECT" -fi - -if [ "$ACTION" = "copy" ] ; then - takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error" - notifyOk "$WHAT copied to buffer" -else - if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then - TITLE="Screenshot of $SUBJECT" - MESSAGE=$(basename "$FILE") - notifyOk "$MESSAGE" "$TITLE" - echo "$FILE" - else - notifyError "Error taking screenshot with grim" - fi -fi diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 deleted file mode 100644 index 2c4c6a95..00000000 --- a/contrib/grimshot.1 +++ /dev/null @@ -1,109 +0,0 @@ -.\" Generated by scdoc 1.11.2 -.\" Complete documentation for this program is not available as a GNU info page -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.nh -.ad l -.\" Begin generated content: -.TH "grimshot" "1" "2022-03-31" -.P -.SH NAME -.P -grimshot - a helper for screenshots within sway -.P -.SH SYNOPSIS -.P -\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE] -.br -\fBgrimshot\fR check -.br -\fBgrimshot\fR usage -.P -.SH OPTIONS -.P -\fB--notify\fR -.RS 4 -Show notifications to the user that a screenshot has been taken.\& -.P -.RE -\fB--cursor\fR -.RS 4 -Include cursors in the screenshot.\& -.P -.RE -\fBsave\fR -.RS 4 -Save the screenshot into a regular file.\& Grimshot will write images -files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined -in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& -Set FILE to '\&-'\& to pipe the output to STDOUT.\& -.P -.RE -\fBcopy\fR -.RS 4 -Copy the screenshot data (as image/png) into the clipboard.\& -.P -.RE -.SH DESCRIPTION -.P -Grimshot is an easy-to-use screenshot utility for sway.\& It provides a -convenient interface over grim, slurp and jq, and supports storing the -screenshot either directly to the clipboard using wl-copy or to a file.\& -.P -.SH EXAMPLES -.P -An example usage pattern is to add these bindings to your sway config: -.P -.nf -.RS 4 -# Screenshots: -# Super+P: Current window -# Super+Shift+p: Select area -# Super+Alt+p Current output -# Super+Ctrl+p Select a window - -bindsym Mod4+p exec grimshot save active -bindsym Mod4+Shift+p exec grimshot save area -bindsym Mod4+Mod1+p exec grimshot save output -bindsym Mod4+Ctrl+p exec grimshot save window -.fi -.RE -.P -.SH TARGETS -.P -grimshot can capture the following named targets: -.P -\fIactive\fR -.RS 4 -Captures the currently active window.\& -.P -.RE -\fIscreen\fR -.RS 4 -Captures the entire screen.\& This includes all visible outputs.\& -.P -.RE -\fIarea\fR -.RS 4 -Allows manually selecting a rectangular region, and captures that.\& -.P -.RE -\fIwindow\fR -.RS 4 -Allows manually selecting a single window (by clicking on it), and -captures it.\& -.P -.RE -\fIoutput\fR -.RS 4 -Captures the currently active output.\& -.P -.RE -.SH OUTPUT -.P -Grimshot will print the filename of the captured screenshot to stdout if called -with the \fIsave\fR subcommand.\& -.P -.SH SEE ALSO -.P -\fBgrim\fR(1) diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd deleted file mode 100644 index e356f99d..00000000 --- a/contrib/grimshot.1.scd +++ /dev/null @@ -1,80 +0,0 @@ -grimshot(1) - -# NAME - -grimshot - a helper for screenshots within sway - -# SYNOPSIS - -*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++ -*grimshot* check++ -*grimshot* usage - -# OPTIONS - -*--notify* - Show notifications to the user that a screenshot has been taken. - -*--cursor* - Include cursors in the screenshot. - -*save* - Save the screenshot into a regular file. Grimshot will write image - files to *XDG_SCREENSHOTS_DIR* if this is set (or defined - in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. - Set FILE to '-' to pipe the output to STDOUT. - -*copy* - Copy the screenshot data (as image/png) into the clipboard. - -# DESCRIPTION - -Grimshot is an easy-to-use screenshot utility for sway. It provides a -convenient interface over grim, slurp and jq, and supports storing the -screenshot either directly to the clipboard using wl-copy or to a file. - -# EXAMPLES - -An example usage pattern is to add these bindings to your sway config: - -``` -# Screenshots: -# Super+P: Current window -# Super+Shift+p: Select area -# Super+Alt+p Current output -# Super+Ctrl+p Select a window - -bindsym Mod4+p exec grimshot save active -bindsym Mod4+Shift+p exec grimshot save area -bindsym Mod4+Mod1+p exec grimshot save output -bindsym Mod4+Ctrl+p exec grimshot save window -``` - -# TARGETS - -grimshot can capture the following named targets: - -_active_ - Captures the currently active window. - -_screen_ - Captures the entire screen. This includes all visible outputs. - -_area_ - Allows manually selecting a rectangular region, and captures that. - -_window_ - Allows manually selecting a single window (by clicking on it), and - captures it. - -_output_ - Captures the currently active output. - -# OUTPUT - -Grimshot will print the filename of the captured screenshot to stdout if called -with the _save_ subcommand. - -# SEE ALSO - -*grim*(1) diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py deleted file mode 100755 index b81134dd..00000000 --- a/contrib/inactive-windows-transparency.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python - -# This script requires i3ipc-python package (install it from a system package manager -# or pip). -# It makes inactive windows transparent. Use `transparency_val` variable to control -# transparency strength in range of 0…1 or use the command line argument -o. - -import argparse -import i3ipc -import signal -import sys -from functools import partial - -def on_window_focus(inactive_opacity, ipc, event): - global prev_focused - global prev_workspace - - focused_workspace = ipc.get_tree().find_focused() - - if focused_workspace == None: - return - - focused = event.container - workspace = focused_workspace.workspace().num - - if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 - focused.command("opacity 1") - if workspace == prev_workspace: - prev_focused.command("opacity " + inactive_opacity) - prev_focused = focused - prev_workspace = workspace - - -def remove_opacity(ipc): - for workspace in ipc.get_tree().workspaces(): - for w in workspace: - w.command("opacity 1") - ipc.main_quit() - sys.exit(0) - - -if __name__ == "__main__": - transparency_val = "0.80" - - parser = argparse.ArgumentParser( - description="This script allows you to set the transparency of unfocused windows in sway." - ) - parser.add_argument( - "--opacity", - "-o", - type=str, - default=transparency_val, - help="set opacity value in range 0...1", - ) - args = parser.parse_args() - - ipc = i3ipc.Connection() - prev_focused = None - prev_workspace = ipc.get_tree().find_focused().workspace().num - - for window in ipc.get_tree(): - if window.focused: - prev_focused = window - else: - window.command("opacity " + args.opacity) - for sig in [signal.SIGINT, signal.SIGTERM]: - signal.signal(sig, lambda signal, frame: remove_opacity(ipc)) - ipc.on("window::focus", partial(on_window_focus, args.opacity)) - ipc.main() \ No newline at end of file diff --git a/flake.nix b/flake.nix index bd3edcea..ab09cb94 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,7 @@ name = "swayfx-shell"; inputsFrom = [ self.packages.${system}.swayfx-unwrapped - pkgs.wlroots_0_16 + pkgs.wlroots_0_17 ]; nativeBuildInputs = with pkgs; [ cmake @@ -55,7 +55,7 @@ shellHook = with pkgs; '' ( mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects" - cp -R --no-preserve=mode,ownership ${wlroots_0_16.src} wlroots + cp -R --no-preserve=mode,ownership ${wlroots_0_17.src} wlroots )''; }; } diff --git a/include/pango.h b/include/pango.h index 1db113c2..228e39cf 100644 --- a/include/pango.h +++ b/include/pango.h @@ -5,6 +5,7 @@ #include #include #include +#include "stringop.h" /** * Utility function which escape characters a & < > ' ". @@ -16,9 +17,9 @@ 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); void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, - int *baseline, double scale, bool markup, const char *fmt, ...); + int *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9); void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); void render_text(cairo_t *cairo, PangoFontDescription *desc, - double scale, bool markup, const char *fmt, ...); + double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); #endif diff --git a/include/stringop.h b/include/stringop.h index b29f59b2..19a50f23 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -5,6 +5,12 @@ #include #include "list.h" +#ifdef __GNUC__ +#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) +#else +#define _SWAY_ATTRIB_PRINTF(start, end) +#endif + void strip_whitespace(char *str); void strip_quotes(char *str); @@ -31,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); // Expand a path using shell replacements such as $HOME and ~ bool expand_path(char **path); +char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); +char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); + #endif diff --git a/include/sway/commands.h b/include/sway/commands.h index b8f5660a..8f21b989 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -3,13 +3,14 @@ #include #include "config.h" +#include "stringop.h" struct sway_container; typedef struct cmd_results *sway_cmd(int argc, char **argv); struct cmd_handler { - char *command; + const char *command; sway_cmd *handle; }; @@ -46,7 +47,7 @@ enum expected_args { struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val); -const struct cmd_handler *find_handler(char *line, +const struct cmd_handler *find_handler(const char *line, const struct cmd_handler *cmd_handlers, size_t handlers_size); /** @@ -76,7 +77,7 @@ struct cmd_results *config_commands_command(char *exec); /** * Allocates a cmd_results object. */ -struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); +struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3); /** * Frees a cmd_results object. */ @@ -174,8 +175,6 @@ sway_cmd cmd_max_render_time; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; -sway_cmd cmd_new_float; -sway_cmd cmd_new_window; sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_saturation; @@ -185,6 +184,7 @@ sway_cmd cmd_no_focus; sway_cmd cmd_output; sway_cmd cmd_permit; sway_cmd cmd_popup_during_fullscreen; +sway_cmd cmd_primary_selection; sway_cmd cmd_reject; sway_cmd cmd_reload; sway_cmd cmd_rename; @@ -290,10 +290,12 @@ sway_cmd input_cmd_map_to_region; sway_cmd input_cmd_middle_emulation; sway_cmd input_cmd_natural_scroll; sway_cmd input_cmd_pointer_accel; +sway_cmd input_cmd_rotation_angle; sway_cmd input_cmd_scroll_factor; sway_cmd input_cmd_repeat_delay; sway_cmd input_cmd_repeat_rate; sway_cmd input_cmd_scroll_button; +sway_cmd input_cmd_scroll_button_lock; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_tap_button_map; diff --git a/include/sway/config.h b/include/sway/config.h index de49bbff..84e4ab8e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -12,8 +12,10 @@ #include "../include/config.h" #include "gesture.h" #include "list.h" +#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" @@ -155,10 +157,12 @@ struct input_config { int middle_emulation; int natural_scroll; float pointer_accel; + float rotation_angle; float scroll_factor; int repeat_delay; int repeat_rate; int scroll_button; + int scroll_button_lock; int scroll_method; int send_events; int tap; @@ -470,15 +474,6 @@ enum xwayland_mode { XWAYLAND_MODE_IMMEDIATE, }; -struct blur_parameters { - int num_passes; - int radius; - float noise; - float brightness; - float contrast; - float saturation; -}; - /** * The configuration struct. The result of loading a config file. */ @@ -501,7 +496,7 @@ struct sway_config { bool blur_enabled; bool blur_xray; - struct blur_parameters blur_params; + struct blur_data blur_params; bool titlebar_separator; bool scratchpad_minimize; @@ -564,6 +559,7 @@ struct sway_config { bool auto_back_and_forth; bool show_marks; enum alignment title_align; + bool primary_selection; bool tiling_drag; int tiling_drag_threshold; @@ -657,7 +653,7 @@ void run_deferred_bindings(void); /** * Adds a warning entry to the swaynag instance used for errors. */ -void config_add_swaynag_warning(char *fmt, ...); +void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); /** * Free config struct diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 59f57f94..8da345ea 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -43,6 +43,7 @@ struct criteria { struct pattern *window_role; enum atom_name window_type; #endif + bool all; bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest diff --git a/include/sway/desktop/fx_renderer/fx_framebuffer.h b/include/sway/desktop/fx_renderer/fx_framebuffer.h deleted file mode 100644 index 3372cd00..00000000 --- a/include/sway/desktop/fx_renderer/fx_framebuffer.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef FX_FRAMEBUFFER_H -#define FX_FRAMEBUFFER_H - -#include -#include -#include - -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_framebuffer { - GLuint fb; - struct fx_stencilbuffer stencil_buffer; - struct fx_texture texture; -}; - -struct fx_framebuffer fx_framebuffer_create(); - -void fx_framebuffer_bind(struct fx_framebuffer *buffer); - -void fx_framebuffer_update(struct fx_framebuffer *buffer, int width, int height); - -void fx_framebuffer_add_stencil_buffer(struct fx_framebuffer *buffer, int width, int height); - -void fx_framebuffer_release(struct fx_framebuffer *buffer); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h deleted file mode 100644 index 7643caaf..00000000 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef _SWAY_OPENGL_H -#define _SWAY_OPENGL_H - -#include -#include -#include -#include - -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -enum corner_location { TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, ALL, NONE }; - -enum fx_tex_shader_source { - SHADER_SOURCE_TEXTURE_RGBA = 1, - SHADER_SOURCE_TEXTURE_RGBX = 2, - SHADER_SOURCE_TEXTURE_EXTERNAL = 3, -}; - -enum fx_rounded_quad_shader_source { - SHADER_SOURCE_QUAD_ROUND = 1, - SHADER_SOURCE_QUAD_ROUND_TOP_LEFT = 2, - SHADER_SOURCE_QUAD_ROUND_TOP_RIGHT = 3, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_RIGHT = 4, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_LEFT = 5, -}; - -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 blur_shader { - GLuint program; - GLint proj; - GLint tex; - GLint pos_attrib; - GLint tex_attrib; - GLint radius; - GLint halfpixel; -}; - -struct effects_shader { - GLuint program; - GLint proj; - GLint tex; - GLint pos_attrib; - GLint tex_attrib; - GLfloat noise; - GLfloat brightness; - GLfloat contrast; - GLfloat saturation; -}; - -struct box_shadow_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint position; - GLint size; - GLint blur_sigma; - GLint corner_radius; -}; - -struct corner_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint is_top_left; - GLint is_top_right; - GLint is_bottom_left; - GLint is_bottom_right; - GLint position; - GLint radius; - GLint half_size; - GLint half_thickness; -}; - -struct quad_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; -}; - -struct rounded_quad_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint size; - GLint position; - GLint radius; -}; - -struct stencil_mask_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint half_size; - GLint position; - GLint radius; -}; - -struct tex_shader { - GLuint program; - GLint proj; - GLint tex; - GLint alpha; - GLint pos_attrib; - GLint tex_attrib; - GLint size; - GLint position; - GLint radius; - GLint saturation; - GLint dim; - GLint dim_color; - GLint has_titlebar; - GLint discard_transparent; -}; - -struct fx_renderer { - float projection[9]; - - int viewport_width, viewport_height; - - struct wlr_output *wlr_output; - - // The framebuffer used by wlroots - struct fx_framebuffer wlr_buffer; - // Contains the blurred background for tiled windows - struct fx_framebuffer blur_buffer; - // Contains the original pixels to draw over the areas where artifact are visible - struct fx_framebuffer blur_saved_pixels_buffer; - // Blur swaps between the two effects buffers everytime it scales the image - // Buffer used for effects - struct fx_framebuffer effects_buffer; - // Swap buffer used for effects - struct fx_framebuffer effects_buffer_swapped; - - // The region where there's blur - pixman_region32_t blur_padding_region; - - bool blur_buffer_dirty; - - struct { - bool OES_egl_image_external; - } exts; - - struct { - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; - } procs; - - struct { - struct box_shadow_shader box_shadow; - struct blur_shader blur1; - struct blur_shader blur2; - struct effects_shader blur_effects; - struct corner_shader corner; - struct quad_shader quad; - struct rounded_quad_shader rounded_quad; - struct rounded_quad_shader rounded_tl_quad; - struct rounded_quad_shader rounded_tr_quad; - struct rounded_quad_shader rounded_bl_quad; - struct rounded_quad_shader rounded_br_quad; - struct stencil_mask_shader stencil_mask; - struct tex_shader tex_rgba; - struct tex_shader tex_rgbx; - struct tex_shader tex_ext; - } shaders; -}; - -struct decoration_data get_undecorated_decoration_data(); - -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *output); - -void fx_renderer_fini(struct fx_renderer *renderer); - -void fx_renderer_begin(struct fx_renderer *renderer, int width, int height); - -void fx_renderer_end(struct fx_renderer *renderer); - -void fx_renderer_clear(const float color[static 4]); - -void fx_renderer_scissor(struct wlr_box *box); - -// Initialize the stenciling work -void fx_renderer_stencil_mask_init(); - -// Close the mask -void fx_renderer_stencil_mask_close(bool draw_inside_mask); - -// Finish stenciling and clear the buffer -void fx_renderer_stencil_mask_fini(); - -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data); - -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_box *dst_box, const float matrix[static 9], struct decoration_data deco_data); - -void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]); - -void fx_render_rounded_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], int radius, - enum corner_location corner_location); - -void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], - enum corner_location corner_location, int radius, int border_thickness); - -void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box, - const struct wlr_box *inner_box, const float color[static 4], - const float matrix[static 9], int corner_radius, float blur_sigma); - -void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, struct blur_shader *shader, - const struct wlr_box *box, int blur_radius); - -void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, - float blur_contrast, float blur_saturation); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_stencilbuffer.h b/include/sway/desktop/fx_renderer/fx_stencilbuffer.h deleted file mode 100644 index 157c0282..00000000 --- a/include/sway/desktop/fx_renderer/fx_stencilbuffer.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FX_STENCILBUFFER_H -#define FX_STENCILBUFFER_H - -#include -#include -#include - -struct fx_stencilbuffer { - GLuint rb; - int width; - int height; -}; - -struct fx_stencilbuffer fx_stencilbuffer_create(); - -void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_texture.h b/include/sway/desktop/fx_renderer/fx_texture.h deleted file mode 100644 index 62e635e6..00000000 --- a/include/sway/desktop/fx_renderer/fx_texture.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FX_TEXTURE_H -#define FX_TEXTURE_H - -#include -#include -#include - -struct fx_texture { - GLuint target; - GLuint id; - bool has_alpha; - int width; - int height; -}; - -struct fx_texture fx_texture_create(); - -struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture *tex); - -void fx_texture_release(struct fx_texture *texture); - -#endif diff --git a/include/sway/desktop/fx_renderer/matrix.h b/include/sway/desktop/fx_renderer/matrix.h deleted file mode 100644 index 6931e8d2..00000000 --- a/include/sway/desktop/fx_renderer/matrix.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _MATRIX_H -#define _MATRIX_H - -#include - -void matrix_projection(float mat[static 9], int width, int height, - enum wl_output_transform transform); - -#endif diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 58d54c68..84cc666d 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -1,8 +1,6 @@ #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #include -#include -#include "sway/server.h" enum sway_idle_inhibit_mode { INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) @@ -16,12 +14,9 @@ struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; struct wl_list inhibitors; - - struct wlr_idle *idle; }; struct sway_idle_inhibitor_v1 { - struct sway_idle_inhibit_manager_v1 *manager; struct wlr_idle_inhibitor_v1 *wlr_inhibitor; struct sway_view *view; enum sway_idle_inhibit_mode mode; @@ -33,8 +28,7 @@ struct sway_idle_inhibitor_v1 { bool sway_idle_inhibit_v1_is_active( struct sway_idle_inhibitor_v1 *inhibitor); -void sway_idle_inhibit_v1_check_active( - struct sway_idle_inhibit_manager_v1 *manager); +void sway_idle_inhibit_v1_check_active(void); void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode); @@ -48,6 +42,6 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi void sway_idle_inhibit_v1_user_inhibitor_destroy( struct sway_idle_inhibitor_v1 *inhibitor); -struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( - struct wl_display *wl_display, struct wlr_idle *idle); +bool sway_idle_inhibit_manager_v1_init(void); + #endif diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h index 3b577f74..412068a9 100644 --- a/include/sway/desktop/launcher.h +++ b/include/sway/desktop/launcher.h @@ -2,14 +2,19 @@ #define _SWAY_LAUNCHER_H #include +#include +#include "sway/input/seat.h" struct launcher_ctx { pid_t pid; - char *name; + char *fallback_name; struct wlr_xdg_activation_token_v1 *token; struct wl_listener token_destroy; + struct sway_seat *seat; + struct wl_listener seat_destroy; bool activated; + bool had_focused_surface; struct sway_node *node; struct wl_listener node_destroy; @@ -25,7 +30,10 @@ void launcher_ctx_consume(struct launcher_ctx *ctx); void launcher_ctx_destroy(struct launcher_ctx *ctx); -struct launcher_ctx *launcher_ctx_create(void); +struct launcher_ctx *launcher_ctx_create_internal(void); + +struct launcher_ctx *launcher_ctx_create( + struct wlr_xdg_activation_token_v1 *token, struct sway_node *node); const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx); diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 8a2898dd..1e21c66f 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -35,7 +35,6 @@ struct sway_cursor { pixman_region32_t confine; // invalid if active_constraint == NULL bool active_confine_requires_warp; - struct wlr_pointer_gestures_v1 *pointer_gestures; struct wl_listener hold_begin; struct wl_listener hold_end; struct wl_listener pinch_begin; @@ -53,6 +52,7 @@ struct sway_cursor { struct wl_listener touch_down; struct wl_listener touch_up; + struct wl_listener touch_cancel; struct wl_listener touch_motion; struct wl_listener touch_frame; bool simulating_pointer_from_touch; @@ -64,6 +64,7 @@ struct sway_cursor { struct wl_listener tool_proximity; struct wl_listener tool_button; bool simulating_pointer_from_tool_tip; + bool simulating_pointer_from_tool_button; uint32_t tool_buttons; struct wl_listener request_set_cursor; @@ -107,6 +108,10 @@ void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); void cursor_notify_key_press(struct sway_cursor *cursor); +void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel); + 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); @@ -140,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error); const char *get_mouse_button_name(uint32_t button); +void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index c9bd08f0..4bd51709 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -25,6 +25,7 @@ struct sway_input_manager { 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 wl_listener new_input; struct wl_listener inhibit_activate; @@ -44,7 +45,7 @@ void input_manager_configure_xcursor(void); void input_manager_apply_input_config(struct input_config *input_config); -void input_manager_configure_all_inputs(void); +void input_manager_configure_all_input_mappings(void); void input_manager_reset_input(struct sway_input_device *input_device); diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h index e4b1acc3..1f84a8e3 100644 --- a/include/sway/input/libinput.h +++ b/include/sway/input/libinput.h @@ -4,6 +4,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *device); +void sway_input_configure_libinput_device_send_events( + struct sway_input_device *device); + void sway_input_reset_libinput_device(struct sway_input_device *device); bool sway_libinput_device_is_builtin(struct sway_input_device *device); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index e3a46872..6063ead3 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "sway/config.h" #include "sway/input/input-manager.h" @@ -11,6 +12,7 @@ #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, @@ -36,14 +38,21 @@ struct sway_seatop_impl { void (*swipe_end)(struct sway_seat *seat, struct wlr_pointer_swipe_end_event *event); void (*rebase)(struct sway_seat *seat, uint32_t time_msec); + void (*touch_motion)(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly); + void (*touch_up)(struct sway_seat *seat, + struct wlr_touch_up_event *event); + void (*touch_down)(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly); + void (*touch_cancel)(struct sway_seat *seat, + struct wlr_touch_cancel_event *event); void (*tablet_tool_motion)(struct sway_seat *seat, struct sway_tablet_tool *tool, uint32_t time_msec); void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, 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 sway_output *output, - pixman_region32_t *damage); + void (*render)(struct sway_seat *seat, struct fx_render_context *ctx); bool allow_set_cursor; }; @@ -72,6 +81,7 @@ struct sway_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; @@ -94,8 +104,9 @@ struct sway_seat { struct sway_workspace *workspace; char *prev_workspace_name; // for workspace back_and_forth - // If the focused layer is set, views cannot receive keyboard focus struct wlr_layer_surface_v1 *focused_layer; + // 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; @@ -157,6 +168,9 @@ void seat_add_device(struct sway_seat *seat, void seat_configure_device(struct sway_seat *seat, struct sway_input_device *device); +void seat_configure_device_mapping(struct sway_seat *seat, + struct sway_input_device *input_device); + void seat_reset_device(struct sway_seat *seat, struct sway_input_device *input_device); @@ -255,10 +269,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont, void seatop_begin_default(struct sway_seat *seat); void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, double sx, double sy); + double sx, double sy); void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, uint32_t time_msec, double sx, double sy); + struct wlr_surface *surface, double sx, double sy); + +void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface, + struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly); void seatop_begin_move_floating(struct sway_seat *seat, struct sway_container *con); @@ -318,6 +335,18 @@ void seatop_swipe_update(struct sway_seat *seat, void seatop_swipe_end(struct sway_seat *seat, struct wlr_pointer_swipe_end_event *event); +void seatop_touch_motion(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly); + +void seatop_touch_up(struct sway_seat *seat, + struct wlr_touch_up_event *event); + +void seatop_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly); + +void seatop_touch_cancel(struct sway_seat *seat, + struct wlr_touch_cancel_event *event); + void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); /** @@ -336,8 +365,7 @@ 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 sway_output *output, - pixman_region32_t *damage); +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 c70fd935..214e61d1 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -4,12 +4,11 @@ #include #include #include -#include "sway/input/seat.h" /** * The relay structure manages the relationship between text-input and * input_method interfaces on a given seat. Multiple text-input interfaces may - * be bound to a relay, but at most one will be focused (reveiving events) at + * be bound to a relay, but at most one will be focused (receiving events) at * a time. At most one input-method interface may be bound to the seat. The * relay manages life cycle of both sides. When both sides are present and * focused, the relay passes messages between them. diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index bc4f781a..d4c00942 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -21,5 +21,6 @@ void ipc_event_mode(const char *mode, bool pango); void ipc_event_shutdown(const char *reason); void ipc_event_binding(struct sway_binding *binding); void ipc_event_input(const char *change, struct sway_input_device *device); +void ipc_event_output(void); #endif diff --git a/include/sway/layers.h b/include/sway/layers.h index 5871f0d8..bb7cbb22 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -60,6 +60,10 @@ struct sway_layer_subsurface { }; struct sway_output; + +struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( + struct wlr_surface *surface); + void arrange_layers(struct sway_output *output); struct sway_layer_surface *layer_from_wlr_layer_surface_v1( diff --git a/include/sway/output.h b/include/sway/output.h index bd65237e..a60ddec8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -6,23 +6,33 @@ #include #include #include "config.h" -#include "sway/desktop/fx_renderer/fx_renderer.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 blur_stencil_data { - struct fx_texture *stencil_texture; - const struct wlr_fbox *stencil_src_box; - float *stencil_matrix; + struct sway_view *view; }; struct sway_output_state { @@ -36,8 +46,6 @@ struct sway_output { struct sway_server *server; struct wl_list link; - struct fx_renderer *renderer; - struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -48,21 +56,20 @@ struct sway_output { int width, height; // transformed buffer size enum wl_output_subpixel detected_subpixel; enum scale_filter_mode scale_filter; - // last applied mode when the output is powered off - struct wlr_output_mode *current_mode; bool enabling, enabled; list_t *workspaces; struct sway_output_state current; + struct wl_listener layout_destroy; struct wl_listener destroy; struct wl_listener commit; - struct wl_listener mode; 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; @@ -72,6 +79,7 @@ struct sway_output { uint32_t refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; + bool gamma_lut_changed; }; struct sway_output_non_desktop { @@ -80,6 +88,14 @@ 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); @@ -111,6 +127,9 @@ 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); + // this ONLY includes the enabled outputs struct sway_output *output_by_name_or_id(const char *name_or_id); @@ -127,8 +146,7 @@ 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 sway_output *output, struct timespec *when, - pixman_region32_t *damage); +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, @@ -181,20 +199,16 @@ 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 sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, +void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, float color[static 4]); -void render_rounded_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4], int corner_radius, - enum corner_location corner_location); - -void render_blur(bool optimized, struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *dst_box, - pixman_region32_t *opaque_region, struct decoration_data *deco_data, - struct blur_stencil_data *stencil_data); +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); @@ -204,6 +218,8 @@ 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); + void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/include/sway/server.h b/include/sway/server.h index 96c3623f..ef70ae4e 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -3,14 +3,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -22,6 +20,7 @@ #include #include "config.h" #include "list.h" +#include "sway/desktop/idle_inhibit_v1.h" #if HAVE_XWAYLAND #include "sway/xwayland.h" #endif @@ -34,9 +33,10 @@ struct sway_server { const char *socket; struct wlr_backend *backend; + struct wlr_session *session; // secondary headless backend used for creating virtual outputs on-the-fly struct wlr_backend *headless_backend; - struct wlr_renderer *wlr_renderer; + struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_compositor *compositor; @@ -51,9 +51,8 @@ struct sway_server { struct wl_listener new_output; struct wl_listener output_layout_change; - struct wlr_idle *idle; struct wlr_idle_notifier_v1 *idle_notifier_v1; - struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; + struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; struct wlr_layer_shell_v1 *layer_shell; struct wl_listener layer_shell_surface; @@ -91,6 +90,9 @@ struct sway_server { struct wl_listener output_manager_apply; struct wl_listener output_manager_test; + struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; + struct wl_listener gamma_control_set_gamma; + struct { bool locked; struct wlr_session_lock_manager_v1 *manager; @@ -110,9 +112,17 @@ struct sway_server { struct wlr_input_method_manager_v2 *input_method; struct wlr_text_input_manager_v3 *text_input; 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; + struct wlr_screencopy_manager_v1 *screencopy_manager_v1; + struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; + struct wlr_security_context_manager_v1 *security_context_manager_v1; struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wl_listener xdg_activation_v1_request_activate; + struct wl_listener xdg_activation_v1_new_token; + + struct wl_listener request_set_cursor_shape; struct wl_list pending_launcher_ctxs; // launcher_ctx::link @@ -174,6 +184,8 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data); void handle_pointer_constraint(struct wl_listener *listener, void *data); void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, void *data); +void xdg_activation_v1_handle_new_token(struct wl_listener *listener, + void *data); void set_rr_scheduling(void); diff --git a/include/sway/surface.h b/include/sway/surface.h index fb1cd775..a7a8ec3f 100644 --- a/include/sway/surface.h +++ b/include/sway/surface.h @@ -15,4 +15,10 @@ struct sway_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/swaynag.h b/include/sway/swaynag.h index 74d9ea18..03bd52c3 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,6 +1,7 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H #include +#include "stringop.h" struct swaynag_instance { struct wl_client *client; @@ -21,7 +22,7 @@ bool swaynag_spawn(const char *swaynag_command, // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, - const char *fmt, ...); + const char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4); // If swaynag->detailed, close swaynag->fd[1] so swaynag displays void swaynag_show(struct swaynag_instance *swaynag); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 3cd668f9..42fb71bf 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -119,6 +119,11 @@ struct sway_container { float saturation; + // Stores last output size and position for adjusting coordinates of + // scratchpad windows. + // Unused for non-scratchpad windows. + struct wlr_box transform; + float alpha; int corner_radius; @@ -206,6 +211,9 @@ size_t container_titlebar_height(void); void floating_calculate_constraints(int *min_width, int *max_width, int *min_height, int *max_height); +void floating_fix_coordinates(struct sway_container *con, + struct wlr_box *old, struct wlr_box *new); + void container_floating_resize_and_center(struct sway_container *con); void container_floating_set_default_size(struct sway_container *con); diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5..03a389a4 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 "list.h" diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7b52aa4b..76cfdf3a 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -2,7 +2,7 @@ #define _SWAY_VIEW_H #include #include -#include "config.h" +#include "sway/config.h" #if HAVE_XWAYLAND #include #endif @@ -162,6 +162,8 @@ struct sway_xwayland_view { struct wl_listener set_window_type; struct wl_listener set_hints; struct wl_listener set_decorations; + struct wl_listener associate; + struct wl_listener dissociate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -179,6 +181,8 @@ struct sway_xwayland_unmanaged { struct wl_listener request_fullscreen; struct wl_listener commit; struct wl_listener set_geometry; + struct wl_listener associate; + struct wl_listener dissociate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -273,7 +277,12 @@ void view_set_activated(struct sway_view *view, bool activated); /** * Called when the view requests to be focused. */ -void view_request_activate(struct sway_view *view); +void view_request_activate(struct sway_view *view, struct sway_seat *seat); + +/* + * Called when the view requests urgent state + */ +void view_request_urgent(struct sway_view *view); /** * If possible, instructs the client to change their decoration mode. diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index d25afbb2..1e7d9b82 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,6 +2,7 @@ #define _SWAY_WORKSPACE_H #include +#include "sway/config.h" #include "sway/tree/container.h" #include "sway/tree/node.h" diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 3ad0bdf3..197d2190 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -4,6 +4,7 @@ #include "config.h" #include "input.h" #include "pool-buffer.h" +#include "cursor-shape-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" @@ -30,6 +31,7 @@ struct swaybar { struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_shm *shm; struct swaybar_config *config; diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index c02a5582..73937a0c 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "swaybar/tray/tray.h" #include "list.h" diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 2d68b6c9..fb9e9c21 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -4,6 +4,8 @@ #include #include "list.h" #include "pool-buffer.h" +#include "cursor-shape-v1-client-protocol.h" + #include "swaynag/types.h" #define SWAYNAG_MAX_HEIGHT 500 @@ -58,6 +60,7 @@ struct swaynag_button { struct swaynag_details { bool visible; char *message; + char *details_text; int x; int y; @@ -67,7 +70,7 @@ struct swaynag_details { int offset; int visible_lines; int total_lines; - struct swaynag_button button_details; + struct swaynag_button *button_details; struct swaynag_button button_up; struct swaynag_button button_down; }; @@ -84,6 +87,7 @@ struct swaynag { struct swaynag_output *output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_surface *surface; uint32_t width; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 18f218e0..9c3c50db 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -1,10 +1,13 @@ #ifndef _SWAYNAG_TYPES_H #define _SWAYNAG_TYPES_H +#include +#include +#include "list.h" + struct swaynag_type { char *name; - char *font; // Used for debugging. PangoFontDescription *font_description; char *output; uint32_t anchors; diff --git a/meson.build b/meson.build index 8d43af84..a8142519 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '0.3.2', + version: '0.3.3', license: 'MIT', meson_version: '>=0.60.0', default_options: [ @@ -18,6 +18,7 @@ add_project_arguments( '-Wno-unused-parameter', '-Wno-unused-result', '-Wno-missing-braces', + '-Wno-format-zero-length', '-Wundef', '-Wvla', ], @@ -35,46 +36,26 @@ if is_freebsd add_project_arguments('-D_C11_SOURCE', language: 'c') endif +# Execute the scenefx subproject, if any +subproject( + 'scenefx', + required: false, +) +scenefx = dependency('scenefx') + # Execute the wlroots subproject, if any -wlroots_version = ['>=0.16.0', '<0.17.0'] +wlroots_version = ['>=0.17.0', '<0.18.0'] subproject( 'wlroots', default_options: ['examples=false'], required: false, version: wlroots_version, ) - -jsonc = dependency('json-c', version: '>=0.13') -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') wlroots = dependency('wlroots', version: wlroots_version) -xkbcommon = dependency('xkbcommon') -cairo = dependency('cairo') -pango = dependency('pango') -pangocairo = dependency('pangocairo') -gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) -pixman = dependency('pixman-1') -glesv2 = dependency('glesv2') -libevdev = dependency('libevdev') -libinput = dependency('libinput', version: '>=1.21.0') -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) -libudev = dependency('libudev') -bash_comp = dependency('bash-completion', required: false) -fish_comp = dependency('fish', required: false) -math = cc.find_library('m') -rt = cc.find_library('rt') -xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) -threads = dependency('threads') # for pthread_setschedparam - wlroots_features = { 'xwayland': false, + 'libinput_backend': false, + 'session': false, } foreach name, _ : wlroots_features var_name = 'have_' + name.underscorify() @@ -85,6 +66,34 @@ 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') +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') +xkbcommon = dependency('xkbcommon', version: '>=1.5.0') +cairo = dependency('cairo') +pango = dependency('pango') +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) +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')) +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' @@ -269,59 +278,7 @@ if get_option('default-wallpaper') install_data(wallpaper_files, install_dir: wallpaper_install_dir) endif -if get_option('zsh-completions') - zsh_files = files( - 'completions/zsh/_sway', - 'completions/zsh/_swaymsg', - ) - zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') - - install_data(zsh_files, install_dir: zsh_install_dir) -endif - -if get_option('bash-completions') - bash_files = files( - 'completions/bash/sway', - 'completions/bash/swaymsg', - ) - - if get_option('swaybar') - bash_files += files('completions/bash/swaybar') - endif - - if bash_comp.found() - bash_install_dir = bash_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') - endif - - install_data(bash_files, install_dir: bash_install_dir) -endif - -if get_option('fish-completions') - fish_files = files( - 'completions/fish/sway.fish', - 'completions/fish/swaymsg.fish', - ) - - if get_option('swaynag') - fish_files += files('completions/fish/swaynag.fish') - endif - - if fish_comp.found() - fish_install_dir = fish_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') - endif - - install_data(fish_files, install_dir: fish_install_dir) -endif +subdir('completions') summary({ 'xwayland': have_xwayland, @@ -329,4 +286,3 @@ summary({ 'tray': have_tray, 'man-pages': scdoc.found(), }, bool_yn: true) - diff --git a/meson_options.txt b/meson_options.txt index 6ba67554..8d0d6509 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for 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 swaybg') +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') option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') diff --git a/protocols/meson.build b/protocols/meson.build index 904aead4..2992ac58 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -2,7 +2,7 @@ wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_scanner_dep = dependency('wayland-scanner', native: true) wayland_scanner = find_program( - wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), + wayland_scanner_dep.get_variable('wayland_scanner'), native: true, ) @@ -12,6 +12,8 @@ protocols = [ 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', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-input-inhibitor-unstable-v1.xml', diff --git a/sway/commands.c b/sway/commands.c index 8b326547..2e048203 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -127,6 +127,7 @@ 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 }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, { "workspace_layout", cmd_workspace_layout }, @@ -171,7 +172,7 @@ static int handler_compare(const void *_a, const void *_b) { return strcasecmp(a->command, b->command); } -const struct cmd_handler *find_handler(char *line, +const struct cmd_handler *find_handler(const char *line, const struct cmd_handler *handlers, size_t handlers_size) { if (!handlers || !handlers_size) { return NULL; @@ -404,10 +405,13 @@ struct cmd_results *config_command(char *exec, char **new_block) { sway_log(SWAY_INFO, "Config command: %s", exec); const struct cmd_handler *handler = find_core_handler(argv[0]); if (!handler || !handler->handle) { - const char *error = handler - ? "Command '%s' is shimmed, but unimplemented" - : "Unknown/invalid command '%s'"; - results = cmd_results_new(CMD_INVALID, error, argv[0]); + if (handler) { + results = cmd_results_new(CMD_INVALID, + "Command '%s' is shimmed, but unimplemented", argv[0]); + } else { + results = cmd_results_new(CMD_INVALID, + "Unknown/invalid command '%s'", argv[0]); + } goto cleanup; } @@ -509,20 +513,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, } results->status = status; if (format) { - char *error = NULL; va_list args; va_start(args, format); - int slen = vsnprintf(NULL, 0, format, args); + results->error = vformat_str(format, args); va_end(args); - if (slen > 0) { - error = malloc(slen + 1); - if (error != NULL) { - va_start(args, format); - vsnprintf(error, slen + 1, format, args); - va_end(args); - } - } - results->error = error; } else { results->error = NULL; } diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 976bc3cc..f7d911f7 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -17,7 +17,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 8571d282..22756acb 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -73,12 +73,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) { } ++argv; --argc; } else if (config->reading && !config->current_bar) { - int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; - id = malloc(len * sizeof(char)); + id = format_str("bar-%d", config->bars->length); if (!id) { return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); } - snprintf(id, len, "bar-%d", config->bars->length); } else if (!config->reading && strcmp(argv[0], "mode") != 0 && strcmp(argv[0], "hidden_state") != 0) { if (is_subcommand(argv[0])) { diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index b4b5bc45..8a837e3f 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, } if (message) { free_bar_binding(binding); - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (!binding->button) { diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 243834ba..3dc9bc4c 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c @@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { } if (message) { free(binding); - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (!binding->button) { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index c0b383db..979e178f 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -127,7 +127,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else { @@ -143,7 +143,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else { @@ -182,7 +182,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, uint32_t button = get_mouse_bindsym(name, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button) { @@ -539,7 +539,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (expected binding with the form " - ":)", bindtype, argc); + ":)", bindtype); } if (strcmp(split->items[0], "tablet") == 0) { binding->type = WLR_SWITCH_TYPE_TABLET_MODE; @@ -549,7 +549,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (expected switch binding: " - "unknown switch %s)", bindtype, split->items[0]); + "unknown switch %s)", bindtype, + (const char *)split->items[0]); } if (strcmp(split->items[1], "on") == 0) { binding->trigger = SWAY_SWITCH_TRIGGER_ON; @@ -562,7 +563,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, return cmd_results_new(CMD_FAILURE, "Invalid %s command " "(expected switch state: unknown state %s)", - bindtype, split->items[1]); + bindtype, (const char *)split->items[1]); } list_free_items_and_destroy(split); diff --git a/sway/commands/blur.c b/sway/commands/blur.c index 5607d1e2..52e3fdb9 100644 --- a/sway/commands/blur.c +++ b/sway/commands/blur.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -21,10 +22,10 @@ struct cmd_results *cmd_blur(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_brightness.c b/sway/commands/blur_brightness.c index 79a40266..6ff60975 100644 --- a/sway/commands/blur_brightness.c +++ b/sway/commands/blur_brightness.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_brightness(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_contrast.c b/sway/commands/blur_contrast.c index 61e3aec4..ba046e63 100644 --- a/sway/commands/blur_contrast.c +++ b/sway/commands/blur_contrast.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_contrast(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_noise.c b/sway/commands/blur_noise.c index bd737911..358b20f5 100644 --- a/sway/commands/blur_noise.c +++ b/sway/commands/blur_noise.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -16,13 +17,13 @@ struct cmd_results *cmd_blur_noise(int argc, char **argv) { config->blur_params.noise = value; - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } - } + 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); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_passes.c b/sway/commands/blur_passes.c index 0868a568..c79f99d1 100644 --- a/sway/commands/blur_passes.c +++ b/sway/commands/blur_passes.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_passes(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_radius.c b/sway/commands/blur_radius.c index f6e7d4ed..85e90e77 100644 --- a/sway/commands/blur_radius.c +++ b/sway/commands/blur_radius.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_radius(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_saturation.c b/sway/commands/blur_saturation.c index 35627363..ae6352d2 100644 --- a/sway/commands/blur_saturation.c +++ b/sway/commands/blur_saturation.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_saturation(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_xray.c b/sway/commands/blur_xray.c index 045566d0..6986b361 100644 --- a/sway/commands/blur_xray.c +++ b/sway/commands/blur_xray.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -15,10 +16,10 @@ struct cmd_results *cmd_blur_xray(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index e6b09e64..8fca1909 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -9,7 +9,6 @@ #include "sway/config.h" #include "sway/server.h" #include "sway/desktop/launcher.h" -#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" @@ -64,7 +63,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { } pid_t pid, child; - struct launcher_ctx *ctx = launcher_ctx_create(); + struct launcher_ctx *ctx = launcher_ctx_create_internal(); // Fork process if ((pid = fork()) == 0) { // Fork child process again diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index 3a1d606a..e8c24ace 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c @@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, char *err; int width = (int)strtol(argv[0], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (strcmp(argv[1], "x") != 0) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } int height = (int)strtol(argv[2], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } *config_width = width; diff --git a/sway/commands/font.c b/sway/commands/font.c index dd80281f..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -33,10 +33,10 @@ struct cmd_results *cmd_font(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid font family."); } - const PangoFontMask flags = pango_font_description_get_set_fields(font_description); - if ((flags & PANGO_FONT_MASK_SIZE) == 0) { + const gint size = pango_font_description_get_size(font_description); + if (size == 0) { pango_font_description_free(font_description); - return cmd_results_new(CMD_FAILURE, "Font size not given."); + return cmd_results_new(CMD_FAILURE, "Invalid font size."); } if (config->font_description != NULL) { diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index ee9f4647..905e6776 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 9a1d8445..43bd6dc8 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (strcmp(argv[0], "none") == 0) { @@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { config->hide_edge_borders = E_NONE; config->hide_edge_borders_smart = ESMART_NO_GAPS; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } config->hide_lone_tab = hide_lone_tab; diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9..6125736a 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c @@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); } else { inhibitor->mode = mode; - sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(); } } else if (!clear) { sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); diff --git a/sway/commands/input.c b/sway/commands/input.c index ea531659..306c40f7 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -25,7 +25,9 @@ static const struct cmd_handler input_handlers[] = { { "pointer_accel", input_cmd_pointer_accel }, { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, + { "rotation_angle", input_cmd_rotation_angle }, { "scroll_button", input_cmd_scroll_button }, + { "scroll_button_lock", input_cmd_scroll_button_lock }, { "scroll_factor", input_cmd_scroll_factor }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 9405181a..08d99bf0 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -1,14 +1,19 @@ #include #include #include -#include +#include #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" #include "log.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static void toggle_supported_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND struct wlr_input_device *wlr_device = input_device->wlr_device; if (!wlr_input_device_is_libinput(wlr_device)) { return; @@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic, } ic->send_events = mode; +#endif } static int mode_for_name(const char *name) { @@ -56,6 +62,7 @@ static int mode_for_name(const char *name) { static void toggle_select_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device, int argc, char **argv) { +#if WLR_HAS_LIBINPUT_BACKEND if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; } @@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, } } ic->send_events = mode_for_name(argv[index % argc]); +#endif } static void toggle_send_events(int argc, char **argv) { diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index de00b714..4400e111 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -11,11 +11,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) { *mm = false; char *end; - *x = strtod(str, &end); - if (end[0] != 'x') { - return false; + + // Check for "0x" prefix to avoid strtod treating the string as hex + if (str[0] == '0' && str[1] == 'x') { + if (strlen(str) < 3) { + return false; + } + *x = 0; + end = (char *)str + 2; + } else { + *x = strtod(str, &end); + if (end[0] != 'x') { + return false; + } + ++end; } - ++end; *y = strtod(end, &end); if (end[0] == 'm') { diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index 284b57d0..ad535db2 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c @@ -49,5 +49,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { error: free(ic->mapped_to_region); ic->mapped_to_region = NULL; - return cmd_results_new(CMD_FAILURE, errstr); + return cmd_results_new(CMD_FAILURE, "%s", errstr); } diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c new file mode 100644 index 00000000..5e278fff --- /dev/null +++ b/sway/commands/input/rotation_angle.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "rotation_angle", 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."); + } + + float rotation_angle = parse_float(argv[0]); + if (isnan(rotation_angle)) { + return cmd_results_new(CMD_INVALID, + "Invalid rotation_angle; expected float."); + } if (rotation_angle < 0 || rotation_angle > 360) { + return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); + } + ic->rotation_angle = rotation_angle; + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 6b331419..81f69a6d 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c @@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { char *message = NULL; uint32_t button = get_mouse_button(*argv, &message); if (message) { - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c new file mode 100644 index 00000000..f96b6514 --- /dev/null +++ b/sway/commands/input/scroll_button_lock.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scroll_button_lock", 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 (parse_boolean(argv[0], true)) { + ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; + } else { + ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 2ba61b38..12ce4839 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->output); } if (new_layout == L_NONE) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (new_layout != old_layout) { if (container) { diff --git a/sway/commands/move.c b/sway/commands/move.c index 7bd1fe3e..69ed06c0 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -206,9 +206,17 @@ static void container_move_to_workspace(struct sway_container *container, container_detach(container); workspace_add_floating(workspace, container); container_handle_fullscreen_reparent(container); - // If changing output, center it within the workspace + // If changing output, adjust the coordinates of the window. if (old_output != workspace->output && !container->pending.fullscreen_mode) { - container_floating_move_to_center(container); + struct wlr_box workspace_box, old_workspace_box; + workspace_get_box(workspace, &workspace_box); + workspace_get_box(old_workspace, &old_workspace_box); + floating_fix_coordinates(container, &old_workspace_box, &workspace_box); + if (container->scratchpad && workspace->output) { + struct wlr_box output_box; + output_get_box(workspace->output, &output_box); + container->transform = workspace_box; + } } } else { container_detach(container); @@ -462,7 +470,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, if (strcasecmp(argv[1], "number") == 0) { // move [window|container] [to] "workspace number x" if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (!isdigit(argv[2][0])) { return cmd_results_new(CMD_INVALID, @@ -522,7 +530,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } destination = &dest_con->node; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (destination->type == N_CONTAINER && @@ -821,7 +829,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } bool absolute = false; @@ -831,19 +839,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (strcmp(argv[0], "position") == 0) { --argc; ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || strcmp(argv[0], "pointer") == 0) { if (absolute) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } return cmd_move_to_position_pointer(container); } else if (strcmp(argv[0], "center") == 0) { @@ -865,7 +873,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (argc < 2) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); } struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; @@ -878,7 +886,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (argc < 1) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); } struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; @@ -887,7 +895,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { argc -= num_consumed_args; argv += num_consumed_args; if (argc > 0) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (ly.unit == MOVEMENT_UNIT_INVALID) { return cmd_results_new(CMD_INVALID, "Invalid y position specified"); @@ -1033,13 +1041,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } // Only `move [window|container] [to] workspace` supports // `--no-auto-back-and-forth` so treat others as invalid syntax if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } if (strcasecmp(argv[0], "workspace") == 0 || @@ -1053,5 +1061,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { strcasecmp(argv[1], "position") == 0)) { return cmd_move_to_position(argc, argv); } - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c index 2001e04f..ccfdec82 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c @@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/output.c b/sway/commands/output.c index df32c673..462dffd2 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -111,7 +111,10 @@ struct cmd_results *cmd_output(int argc, char **argv) { if (!config->reloading && !config->validating) { apply_output_config_to_outputs(output); if (background) { - spawn_swaybg(); + if (!spawn_swaybg()) { + return cmd_results_new(CMD_FAILURE, + "Failed to apply background configuration"); + } } } diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 67f212ff..d691295f 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -123,7 +123,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { src); config_add_swaynag_warning("Unable to access background file '%s'", src); + struct cmd_results *result = cmd_results_new(CMD_FAILURE, + "unable to access background file '%s'", src); free(src); + return result; } else { output->background = src; output->background_option = strdup(mode); diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c new file mode 100644 index 00000000..585b079d --- /dev/null +++ b/sway/commands/primary_selection.c @@ -0,0 +1,23 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "util.h" + +struct cmd_results *cmd_primary_selection(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + bool primary_selection = parse_boolean(argv[0], true); + + 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); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 60a66d58..0d36cc21 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -26,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { "Can't run this command while there's no outputs connected."); } if (strcasecmp(argv[0], "workspace") != 0) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } int argn = 1; @@ -65,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { ++argn; // move past "to" if (argn >= argc) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } char *new_name = join_args(argv + argn, argc - argn); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 425069de..32b746ea 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con, return; } + if (container_is_scratchpad_hidden_or_child(con)) { + return; + } + // For HORIZONTAL or VERTICAL, we are growing in two directions so select // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to @@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, struct movement_amount *amount) { struct sway_container *current = config->handler_context.container; + if (container_is_scratchpad_hidden_or_child(current)) { + return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); + } + if (amount->unit == MOVEMENT_UNIT_DEFAULT) { amount->unit = MOVEMENT_UNIT_PPT; } if (amount->unit == MOVEMENT_UNIT_PPT) { + struct sway_container *parent = current->pending.parent; float pct = amount->amount / 100.0f; if (is_horizontal(axis)) { - amount->amount = (float)current->pending.width * pct; + while (parent && parent->pending.layout != L_HORIZ) { + parent = parent->pending.parent; + } + if (parent) { + amount->amount = (float)parent->pending.width * pct; + } else { + amount->amount = (float)current->pending.workspace->width * pct; + } } else { - amount->amount = (float)current->pending.height * pct; + while (parent && parent->pending.layout != L_VERT) { + parent = parent->pending.parent; + } + if (parent) { + amount->amount = (float)parent->pending.height * pct; + } else { + amount->amount = (float)current->pending.workspace->height * pct; + } } } @@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, */ static struct cmd_results *resize_set_tiled(struct sway_container *con, struct movement_amount *width, struct movement_amount *height) { + + if (container_is_scratchpad_hidden_or_child(con)) { + return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); + } + if (width->amount) { if (width->unit == MOVEMENT_UNIT_PPT || width->unit == MOVEMENT_UNIT_DEFAULT) { @@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { argc -= num_consumed_args; argv += num_consumed_args; if (width.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } @@ -427,10 +455,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { } int num_consumed_args = parse_movement_amount(argc, argv, &height); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (width.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } @@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, "[ px|ppt [or px|ppt]]'"; uint32_t axis = parse_resize_axis(*argv); if (axis == WLR_EDGE_NONE) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } --argc; ++argv; @@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, argc -= num_consumed_args; argv += num_consumed_args; if (first_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } else { first_amount.amount = 10; @@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, // "or" if (argc) { if (strcmp(*argv, "or") != 0) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } --argc; ++argv; } @@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, if (argc) { int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (second_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } else { second_amount.amount = 0; @@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) { const char usage[] = "Expected 'resize " " [] [px|ppt]'"; - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 504a9f5e..5a8a3bc8 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -18,7 +18,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { if (strcasecmp(argv[0], "move") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } int delta_x = strtol(argv[1], NULL, 10); int delta_y = strtol(argv[2], NULL, 10); @@ -27,7 +27,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else if (strcasecmp(argv[0], "set") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } // map absolute coords (0..1,0..1) to root container coords float x = strtof(argv[1], NULL) / root->width; @@ -37,7 +37,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else { if (argc < 2) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } struct cmd_results *error = NULL; if ((error = press_or_release(cursor, argv[0], argv[1]))) { @@ -92,14 +92,14 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, } else if (strcasecmp(action, "release") == 0) { state = WLR_BUTTON_RELEASED; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } char *message = NULL; button = get_mouse_button(button_str, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 82428f2c..62b94db2 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c @@ -3,6 +3,7 @@ #include #include #include +#include "log.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/input/seat.h" @@ -69,5 +70,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid idle source"); } config->handler_context.seat_config->idle_wake_sources = sources; + sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); + if (config->reading) { + config_add_swaynag_warning("seat idle_wake is deprecated. " + "Only seat idle_inhibit is supported."); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/shadows.c b/sway/commands/shadows.c index ae7fc019..a213de8f 100644 --- a/sway/commands/shadows.c +++ b/sway/commands/shadows.c @@ -4,8 +4,6 @@ #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/container.h" -#include "log.h" -#include "stringop.h" #include "util.h" struct cmd_results *cmd_shadows(int argc, char **argv) { diff --git a/sway/commands/shadows_on_csd.c b/sway/commands/shadows_on_csd.c index c9f56dd1..24f2bea4 100644 --- a/sway/commands/shadows_on_csd.c +++ b/sway/commands/shadows_on_csd.c @@ -2,10 +2,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/arrange.h" -#include "sway/tree/view.h" -#include "sway/tree/container.h" -#include "log.h" -#include "stringop.h" #include "util.h" struct cmd_results *cmd_shadows_on_csd(int argc, char **argv) { diff --git a/sway/commands/split.c b/sway/commands/split.c index c8a2cfc1..500a497d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -32,7 +32,7 @@ static struct cmd_results *do_split(int layout) { return cmd_results_new(CMD_SUCCESS, NULL); } -static struct cmd_results *do_unsplit() { +static struct cmd_results *do_unsplit(void) { struct sway_container *con = config->handler_context.container; struct sway_workspace *ws = config->handler_context.workspace; diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 9355944d..d44eb006 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -5,6 +5,7 @@ #include "sway/commands.h" #include "sway/output.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" @@ -13,180 +14,6 @@ static const char expected_syntax[] = "Expected 'swap container with id|con_id|mark '"; -static void swap_places(struct sway_container *con1, - struct sway_container *con2) { - struct sway_container *temp = malloc(sizeof(struct sway_container)); - temp->pending.x = con1->pending.x; - temp->pending.y = con1->pending.y; - temp->pending.width = con1->pending.width; - temp->pending.height = con1->pending.height; - temp->width_fraction = con1->width_fraction; - temp->height_fraction = con1->height_fraction; - temp->pending.parent = con1->pending.parent; - temp->pending.workspace = con1->pending.workspace; - bool temp_floating = container_is_floating(con1); - - con1->pending.x = con2->pending.x; - con1->pending.y = con2->pending.y; - con1->pending.width = con2->pending.width; - con1->pending.height = con2->pending.height; - con1->width_fraction = con2->width_fraction; - con1->height_fraction = con2->height_fraction; - - con2->pending.x = temp->pending.x; - con2->pending.y = temp->pending.y; - con2->pending.width = temp->pending.width; - con2->pending.height = temp->pending.height; - con2->width_fraction = temp->width_fraction; - con2->height_fraction = temp->height_fraction; - - int temp_index = container_sibling_index(con1); - if (con2->pending.parent) { - container_insert_child(con2->pending.parent, con1, - container_sibling_index(con2)); - } else if (container_is_floating(con2)) { - workspace_add_floating(con2->pending.workspace, con1); - } else { - workspace_insert_tiling(con2->pending.workspace, con1, - container_sibling_index(con2)); - } - if (temp->pending.parent) { - container_insert_child(temp->pending.parent, con2, temp_index); - } else if (temp_floating) { - workspace_add_floating(temp->pending.workspace, con2); - } else { - workspace_insert_tiling(temp->pending.workspace, con2, temp_index); - } - - free(temp); -} - -static void swap_focus(struct sway_container *con1, - struct sway_container *con2, struct sway_seat *seat, - struct sway_container *focus) { - if (focus == con1 || focus == con2) { - struct sway_workspace *ws1 = con1->pending.workspace; - struct sway_workspace *ws2 = con2->pending.workspace; - enum sway_container_layout layout1 = container_parent_layout(con1); - enum sway_container_layout layout2 = container_parent_layout(con2); - if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { - if (workspace_is_visible(ws2)) { - seat_set_focus(seat, &con2->node); - } - seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); - } else if (focus == con2 && (layout1 == L_TABBED - || layout1 == L_STACKED)) { - if (workspace_is_visible(ws1)) { - seat_set_focus(seat, &con1->node); - } - seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); - } else if (ws1 != ws2) { - seat_set_focus_container(seat, focus == con1 ? con2 : con1); - } else { - seat_set_focus_container(seat, focus); - } - } else { - seat_set_focus_container(seat, focus); - } - - if (root->fullscreen_global) { - seat_set_focus(seat, - seat_get_focus_inactive(seat, &root->fullscreen_global->node)); - } -} - -void container_swap(struct sway_container *con1, struct sway_container *con2) { - if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { - return; - } - if (!sway_assert(!container_has_ancestor(con1, con2) - && !container_has_ancestor(con2, con1), - "Cannot swap ancestor and descendant")) { - return; - } - - sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", - con1->node.id, con2->node.id); - - bool scratch1 = con1->scratchpad; - bool hidden1 = container_is_scratchpad_hidden(con1); - bool scratch2 = con2->scratchpad; - bool hidden2 = container_is_scratchpad_hidden(con2); - if (scratch1) { - if (hidden1) { - root_scratchpad_show(con1); - } - root_scratchpad_remove_container(con1); - } - if (scratch2) { - if (hidden2) { - root_scratchpad_show(con2); - } - root_scratchpad_remove_container(con2); - } - - enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; - if (fs1) { - container_fullscreen_disable(con1); - } - enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; - if (fs2) { - container_fullscreen_disable(con2); - } - - struct sway_seat *seat = config->handler_context.seat; - struct sway_container *focus = seat_get_focused_container(seat); - struct sway_workspace *vis1 = - output_get_active_workspace(con1->pending.workspace->output); - struct sway_workspace *vis2 = - output_get_active_workspace(con2->pending.workspace->output); - if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" - "workspace. This should not happen")) { - return; - } - - char *stored_prev_name = NULL; - if (seat->prev_workspace_name) { - stored_prev_name = strdup(seat->prev_workspace_name); - } - - swap_places(con1, con2); - - if (!workspace_is_visible(vis1)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); - } - if (!workspace_is_visible(vis2)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); - } - - swap_focus(con1, con2, seat, focus); - - if (stored_prev_name) { - free(seat->prev_workspace_name); - seat->prev_workspace_name = stored_prev_name; - } - - if (scratch1) { - root_scratchpad_add_container(con2, NULL); - if (!hidden1) { - root_scratchpad_show(con2); - } - } - if (scratch2) { - root_scratchpad_add_container(con1, NULL); - if (!hidden2) { - root_scratchpad_show(con1); - } - } - - if (fs1) { - container_set_fullscreen(con2, fs1); - } - if (fs2) { - container_set_fullscreen(con1, fs2); - } -} - static bool test_con_id(struct sway_container *container, void *data) { size_t *con_id = data; return container->node.id == *con_id; @@ -219,7 +46,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } struct sway_container *current = config->handler_context.container; @@ -238,7 +65,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { other = root_find_container(test_mark, value); } else { free(value); - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (!other) { diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index a6a0beda..03e488ba 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -61,7 +61,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, const char expected[] = "Expected 'workspace gaps " "inner|outer|horizontal|vertical|top|right|bottom|left '"; if (gaps_location == 0) { - return cmd_results_new(CMD_INVALID, expected); + return cmd_results_new(CMD_INVALID, "%s", expected); } struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, @@ -79,7 +79,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, char *end; int amount = strtol(argv[gaps_location + 2], &end, 10); if (strlen(end)) { - return cmd_results_new(CMD_FAILURE, expected); + return cmd_results_new(CMD_FAILURE, "%s", expected); } bool valid = false; @@ -110,7 +110,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, } } if (!valid) { - return cmd_results_new(CMD_INVALID, expected); + return cmd_results_new(CMD_INVALID, "%s", expected); } // Prevent invalid gaps configurations. @@ -174,7 +174,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } if (root->fullscreen_global) { - return cmd_results_new(CMD_FAILURE, "workspace", + return cmd_results_new(CMD_FAILURE, "Can't switch workspaces while fullscreen global"); } diff --git a/sway/config.c b/sway/config.c index 0203080c..616965c1 100644 --- a/sway/config.c +++ b/sway/config.c @@ -38,7 +38,7 @@ 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_FLAGS); + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( context, &rules, @@ -280,6 +280,7 @@ static void config_defaults(struct sway_config *config) { config->title_align = ALIGN_LEFT; config->tiling_drag = true; config->tiling_drag_threshold = 9; + config->primary_selection = true; config->smart_gaps = SMART_GAPS_OFF; config->gaps_inner = 0; @@ -511,6 +512,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { old_config->xwayland ? "enabled" : "disabled"); config->xwayland = old_config->xwayland; + // primary_selection can only be enabled/disabled at launch + sway_log(SWAY_DEBUG, "primary_selection will remain %s", + old_config->primary_selection ? "enabled" : "disabled"); + config->primary_selection = old_config->primary_selection; + if (!config->validating) { if (old_config->swaybg_client != NULL) { wl_client_destroy(old_config->swaybg_client); @@ -960,23 +966,18 @@ void config_add_swaynag_warning(char *fmt, ...) { if (config->reading && !config->validating) { va_list args; va_start(args, fmt); - size_t length = vsnprintf(NULL, 0, fmt, args) + 1; + char *str = vformat_str(fmt, args); va_end(args); - - char *temp = malloc(length + 1); - if (!temp) { - sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); + if (str == NULL) { return; } - va_start(args, fmt); - vsnprintf(temp, length, fmt, args); - va_end(args); - swaynag_log(config->swaynag_command, &config->swaynag_config_errors, "Warning on line %i (%s) '%s': %s", config->current_config_line_number, config->current_config_path, - config->current_config_line, temp); + config->current_config_line, str); + + free(str); } } @@ -1016,7 +1017,7 @@ char *do_var_replacement(char *str) { int offset = find - str; strncpy(newptr, str, offset); newptr += offset; - strncpy(newptr, var->value, vvlen); + memcpy(newptr, var->value, vvlen); newptr += vvlen; strcpy(newptr, find + vnlen); free(str); diff --git a/sway/config/bar.c b/sway/config/bar.c index d1b342e6..a8389244 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -256,7 +256,6 @@ static void invoke_swaybar(struct bar_config *bar) { } sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); - return; } void load_swaybar(struct bar_config *bar) { diff --git a/sway/config/input.c b/sway/config/input.c index a98204df..44c2be28 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -31,9 +31,11 @@ struct input_config *new_input_config(const char* identifier) { input->middle_emulation = INT_MIN; input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; + input->rotation_angle = FLT_MIN; input->pointer_accel = FLT_MIN; input->scroll_factor = FLT_MIN; input->scroll_button = INT_MIN; + input->scroll_button_lock = INT_MIN; input->scroll_method = INT_MIN; input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; @@ -74,6 +76,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->natural_scroll != INT_MIN) { dst->natural_scroll = src->natural_scroll; } + if (src->rotation_angle != FLT_MIN) { + dst->rotation_angle = src->rotation_angle; + } if (src->pointer_accel != FLT_MIN) { dst->pointer_accel = src->pointer_accel; } @@ -92,6 +97,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->scroll_button != INT_MIN) { dst->scroll_button = src->scroll_button; } + if (src->scroll_button_lock != INT_MIN) { + dst->scroll_button_lock = src->scroll_button_lock; + } if (src->send_events != INT_MIN) { dst->send_events = src->send_events; } diff --git a/sway/config/output.c b/sway/config/output.c index 9c7082d0..3c1822d8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -6,10 +6,10 @@ #include #include #include +#include #include #include #include -#include #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" @@ -17,6 +17,10 @@ #include "log.h" #include "util.h" +#if WLR_HAS_DRM_BACKEND +#include +#endif + int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; const char *name = data; @@ -149,25 +153,16 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { } static void merge_id_on_name(struct output_config *oc) { - char *id_on_name = NULL; - char id[128]; - char *name = NULL; - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - name = output->wlr_output->name; - output_get_identifier(id, sizeof(id), output); - if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { - size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; - id_on_name = malloc(length); - if (!id_on_name) { - sway_log(SWAY_ERROR, "Failed to allocate id on name string"); - return; - } - snprintf(id_on_name, length, "%s on %s", id, name); - break; - } + 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; } @@ -253,7 +248,9 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending // Not all floating point integers can be represented exactly // as (int)(1000 * mHz / 1000.f) // round() the result to avoid any error - int mhz = (int)round(refresh_rate * 1000); + int mhz = (int)roundf(refresh_rate * 1000); + // If no target refresh rate is given, match highest available + mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); @@ -263,29 +260,35 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } struct wlr_output_mode *mode, *best = NULL; + int best_diff_mhz = INT_MAX; wl_list_for_each(mode, &output->modes, link) { if (mode->width == width && mode->height == height) { - if (mode->refresh == mhz) { - best = mode; - break; - } - if (best == NULL || mode->refresh > best->refresh) { + int diff_mhz = abs(mode->refresh - mhz); + if (diff_mhz < best_diff_mhz) { + best_diff_mhz = diff_mhz; best = mode; + if (best_diff_mhz == 0) { + break; + } } } } - if (!best) { - sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); - sway_log(SWAY_INFO, "Picking preferred mode instead"); - best = wlr_output_preferred_mode(output); + 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 { - sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); + best = wlr_output_preferred_mode(output); + sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " + "applying preferred mode (%dx%d@%.3fHz)", + width, height, refresh_rate, + best->width, best->height, best->refresh / 1000.f); } wlr_output_state_set_mode(pending, best); } static void set_modeline(struct wlr_output *output, struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { +#if WLR_HAS_DRM_BACKEND if (!wlr_output_is_drm(output)) { sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; @@ -295,6 +298,9 @@ static void set_modeline(struct wlr_output *output, if (mode) { wlr_output_state_set_mode(pending, mode); } +#else + sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); +#endif } /* Some manufacturers hardcode the aspect-ratio of the output in the physical @@ -436,9 +442,11 @@ static void queue_output_config(struct output_config *oc, enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { tr = 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); +#endif } if (wlr_output->transform != tr) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); @@ -450,6 +458,16 @@ static void queue_output_config(struct output_config *oc, float scale; 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; + } } else { scale = compute_default_scale(wlr_output, pending); sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); @@ -499,10 +517,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { struct wlr_output_state pending = {0}; queue_output_config(oc, output, &pending); - if (!oc || oc->power != 0) { - output->current_mode = pending.mode; - } - 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. @@ -576,7 +590,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { // 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_inputs(); + input_manager_configure_all_input_mappings(); // Reconfigure the cursor images, since the scale may have changed. input_manager_configure_xcursor(); return true; @@ -619,9 +633,7 @@ static struct output_config *get_output_config(char *identifier, struct output_config *oc_name = NULL; struct output_config *oc_id = NULL; - size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; - char *id_on_name = malloc(length); - snprintf(id_on_name, length, "%s on %s", identifier, name); + 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]; @@ -708,12 +720,11 @@ void apply_output_config_to_outputs(struct output_config *oc) { // 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; - char id[128]; struct sway_output *sway_output, *tmp; wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { - char *name = sway_output->wlr_output->name; - output_get_identifier(id, sizeof(id), sway_output); - if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { + 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 @@ -814,7 +825,9 @@ static bool _spawn_swaybg(char **command) { setenv("WAYLAND_SOCKET", wayland_socket_str, true); execvp(command[0], command); - sway_log_errno(SWAY_ERROR, "execvp failed"); + sway_log_errno(SWAY_ERROR, "failed to execute '%s' " + "(background configuration probably not applied)", + command[0]); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); @@ -824,12 +837,13 @@ static bool _spawn_swaybg(char **command) { sway_log_errno(SWAY_ERROR, "close failed"); return false; } - if (waitpid(pid, NULL, 0) < 0) { + int fork_status = 0; + if (waitpid(pid, &fork_status, 0) < 0) { sway_log_errno(SWAY_ERROR, "waitpid failed"); return false; } - return true; + return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; } bool spawn_swaybg(void) { diff --git a/sway/config/seat.c b/sway/config/seat.c index 84260aa3..6d5d91ae 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -99,7 +99,6 @@ static void seat_attachment_config_free( struct seat_attachment_config *attachment) { free(attachment->identifier); free(attachment); - return; } static struct seat_attachment_config *seat_attachment_config_copy( diff --git a/sway/criteria.c b/sway/criteria.c index d7326bea..78ea8b8a 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -19,6 +19,7 @@ bool criteria_is_empty(struct criteria *criteria) { return !criteria->title && !criteria->shell + && !criteria->all && !criteria->app_id && !criteria->con_mark && !criteria->con_id @@ -456,6 +457,7 @@ static enum atom_name parse_window_type(const char *type) { #endif enum criteria_token { + T_ALL, T_APP_ID, T_CON_ID, T_CON_MARK, @@ -478,7 +480,9 @@ enum criteria_token { }; static enum criteria_token token_from_name(char *name) { - if (strcmp(name, "app_id") == 0) { + if (strcmp(name, "all") == 0) { + return T_ALL; + } else if (strcmp(name, "app_id") == 0) { return T_APP_ID; } else if (strcmp(name, "con_id") == 0) { return T_CON_ID; @@ -524,8 +528,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { return false; } - // Require value, unless token is floating or tiled - if (!value && token != T_FLOATING && token != T_TILING) { + // Require value, unless token is all, floating or tiled + if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { const char *fmt = "Token '%s' requires a value"; int len = strlen(fmt) + strlen(name) - 1; error = malloc(len); @@ -535,6 +539,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *endptr = NULL; switch (token) { + case T_ALL: + criteria->all = true; + break; case T_TITLE: pattern_create(&criteria->title, value); break; diff --git a/sway/desktop/fx_renderer/fx_framebuffer.c b/sway/desktop/fx_renderer/fx_framebuffer.c deleted file mode 100644 index dd8c27b1..00000000 --- a/sway/desktop/fx_renderer/fx_framebuffer.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "log.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_framebuffer fx_framebuffer_create() { - return (struct fx_framebuffer) { - .fb = -1, - .stencil_buffer = fx_stencilbuffer_create(), - .texture = fx_texture_create(), - }; -} - -void fx_framebuffer_bind(struct fx_framebuffer *buffer) { - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); -} - -void fx_framebuffer_update(struct fx_framebuffer *buffer, int width, int height) { - bool first_alloc = false; - - if (buffer->fb == (uint32_t) -1) { - glGenFramebuffers(1, &buffer->fb); - first_alloc = true; - } - - if (buffer->texture.id == 0) { - first_alloc = true; - glGenTextures(1, &buffer->texture.id); - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - if (first_alloc || buffer->texture.width != width || buffer->texture.height != height) { - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - buffer->texture.id, 0); - buffer->texture.target = GL_TEXTURE_2D; - buffer->texture.has_alpha = false; - buffer->texture.width = width; - buffer->texture.height = height; - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - sway_log(SWAY_ERROR, "Framebuffer incomplete, couldn't create! (FB status: %i)", status); - return; - } - sway_log(SWAY_DEBUG, "Framebuffer created, status %i", status); - } - - glBindTexture(GL_TEXTURE_2D, 0); -} - -void fx_framebuffer_add_stencil_buffer(struct fx_framebuffer *buffer, int width, int height) { - bool first_alloc = false; - - if (buffer->stencil_buffer.rb == (uint32_t) -1) { - glGenRenderbuffers(1, &buffer->stencil_buffer.rb); - first_alloc = true; - } - - if (first_alloc || buffer->stencil_buffer.width != width || buffer->stencil_buffer.height != height) { - glBindRenderbuffer(GL_RENDERBUFFER, buffer->stencil_buffer.rb); - glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); - buffer->stencil_buffer.width = width; - buffer->stencil_buffer.height = height; - } - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->stencil_buffer.rb); -} - -void fx_framebuffer_release(struct fx_framebuffer *buffer) { - // Release the framebuffer - if (buffer->fb != (uint32_t) -1 && buffer->fb) { - glDeleteFramebuffers(1, &buffer->fb); - } - buffer->fb = -1; - - // Release the stencil buffer - fx_stencilbuffer_release(&buffer->stencil_buffer); - - // Release the texture - fx_texture_release(&buffer->texture); -} diff --git a/sway/desktop/fx_renderer/fx_renderer.c b/sway/desktop/fx_renderer/fx_renderer.c deleted file mode 100644 index f06d93b5..00000000 --- a/sway/desktop/fx_renderer/fx_renderer.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - The original wlr_renderer was heavily referenced in making this project - https://gitlab.freedesktop.org/wlroots/wlroots/-/tree/master/render/gles2 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" -#include "sway/desktop/fx_renderer/matrix.h" -#include "sway/server.h" - -// shaders -#include "blur1_frag_src.h" -#include "blur2_frag_src.h" -#include "blur_effects_frag_src.h" -#include "box_shadow_frag_src.h" -#include "common_vert_src.h" -#include "corner_frag_src.h" -#include "quad_frag_src.h" -#include "quad_round_frag_src.h" -#include "stencil_mask_frag_src.h" -#include "tex_frag_src.h" - -static const GLfloat verts[] = { - 1, 0, // top right - 0, 0, // top left - 1, 1, // bottom right - 0, 1, // bottom left -}; - -static GLuint compile_shader(GLuint type, const GLchar *src) { - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &src, NULL); - glCompileShader(shader); - - GLint ok; - glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); - if (ok == GL_FALSE) { - sway_log(SWAY_ERROR, "Failed to compile shader"); - glDeleteShader(shader); - shader = 0; - } - - return shader; -} - -static GLuint link_program(const GLchar *frag_src) { - const GLchar *vert_src = common_vert_src; - GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src); - if (!vert) { - goto error; - } - - GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src); - if (!frag) { - glDeleteShader(vert); - goto error; - } - - GLuint prog = glCreateProgram(); - glAttachShader(prog, vert); - glAttachShader(prog, frag); - glLinkProgram(prog); - - glDetachShader(prog, vert); - glDetachShader(prog, frag); - glDeleteShader(vert); - glDeleteShader(frag); - - GLint ok; - glGetProgramiv(prog, GL_LINK_STATUS, &ok); - if (ok == GL_FALSE) { - sway_log(SWAY_ERROR, "Failed to link shader"); - glDeleteProgram(prog); - goto error; - } - - return prog; - -error: - return 0; -} - -static bool link_blur_program(struct blur_shader *shader, const char *shader_program) { - GLuint prog; - shader->program = prog = link_program(shader_program); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->halfpixel = glGetUniformLocation(prog, "halfpixel"); - - return true; -} - -static bool link_blur_effects_program(struct effects_shader *shader, const char *shader_program) { - GLuint prog; - shader->program = prog = link_program(shader_program); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->noise = glGetUniformLocation(prog, "noise"); - shader->brightness = glGetUniformLocation(prog, "brightness"); - shader->contrast = glGetUniformLocation(prog, "contrast"); - shader->saturation = glGetUniformLocation(prog, "saturation"); - - return true; - -} - -static bool link_box_shadow_program(struct box_shadow_shader *shader) { - GLuint prog; - shader->program = prog = link_program(box_shadow_frag_src); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->size = glGetUniformLocation(prog, "size"); - shader->blur_sigma = glGetUniformLocation(prog, "blur_sigma"); - shader->corner_radius = glGetUniformLocation(prog, "corner_radius"); - - return true; -} - -static bool link_corner_program(struct corner_shader *shader) { - GLuint prog; - shader->program = prog = link_program(corner_frag_src); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->half_size = glGetUniformLocation(prog, "half_size"); - shader->half_thickness = glGetUniformLocation(prog, "half_thickness"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->is_top_left = glGetUniformLocation(prog, "is_top_left"); - shader->is_top_right = glGetUniformLocation(prog, "is_top_right"); - shader->is_bottom_left = glGetUniformLocation(prog, "is_bottom_left"); - shader->is_bottom_right = glGetUniformLocation(prog, "is_bottom_right"); - - return true; -} - -static bool link_quad_program(struct quad_shader *shader) { - GLuint prog; - shader->program = prog = link_program(quad_frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - - return true; -} - -static bool link_rounded_quad_program(struct rounded_quad_shader *shader, - enum fx_rounded_quad_shader_source source) { - GLchar quad_src[2048]; - snprintf(quad_src, sizeof(quad_src), - "#define SOURCE %d\n%s", source, quad_round_frag_src); - - GLuint prog; - shader->program = prog = link_program(quad_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->size = glGetUniformLocation(prog, "size"); - shader->position = glGetUniformLocation(prog, "position"); - shader->radius = glGetUniformLocation(prog, "radius"); - - return true; -} - -static bool link_stencil_mask_program(struct stencil_mask_shader *shader) { - GLuint prog; - shader->program = prog = link_program(stencil_mask_frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->half_size = glGetUniformLocation(prog, "half_size"); - shader->radius = glGetUniformLocation(prog, "radius"); - - return true; -} - -static bool link_tex_program(struct tex_shader *shader, - enum fx_tex_shader_source source) { - GLchar frag_src[2048]; - snprintf(frag_src, sizeof(frag_src), - "#define SOURCE %d\n%s", source, tex_frag_src); - - GLuint prog; - shader->program = prog = link_program(frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->alpha = glGetUniformLocation(prog, "alpha"); - shader->dim = glGetUniformLocation(prog, "dim"); - shader->dim_color = glGetUniformLocation(prog, "dim_color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->size = glGetUniformLocation(prog, "size"); - shader->position = glGetUniformLocation(prog, "position"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->saturation = glGetUniformLocation(prog, "saturation"); - shader->has_titlebar = glGetUniformLocation(prog, "has_titlebar"); - shader->discard_transparent = glGetUniformLocation(prog, "discard_transparent"); - - return true; -} - -static bool check_gl_ext(const char *exts, const char *ext) { - size_t extlen = strlen(ext); - const char *end = exts + strlen(exts); - - while (exts < end) { - if (exts[0] == ' ') { - exts++; - continue; - } - size_t n = strcspn(exts, " "); - if (n == extlen && strncmp(ext, exts, n) == 0) { - return true; - } - exts += n; - } - return false; -} - -static void load_gl_proc(void *proc_ptr, const char *name) { - void *proc = (void *)eglGetProcAddress(name); - if (proc == NULL) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: eglGetProcAddress(%s) failed", name); - abort(); - } - *(void **)proc_ptr = proc; -} - -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *wlr_output) { - struct fx_renderer *renderer = calloc(1, sizeof(struct fx_renderer)); - if (renderer == NULL) { - return NULL; - } - - renderer->wlr_output = wlr_output; - - // TODO: wlr_egl_make_current or eglMakeCurrent? - // TODO: assert instead of conditional statement? - if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE, - wlr_egl_get_context(egl))) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current"); - return NULL; - } - - renderer->wlr_buffer = fx_framebuffer_create(); - renderer->blur_buffer = fx_framebuffer_create(); - renderer->blur_saved_pixels_buffer = fx_framebuffer_create(); - renderer->effects_buffer = fx_framebuffer_create(); - renderer->effects_buffer_swapped = fx_framebuffer_create(); - - renderer->blur_buffer_dirty = true; - - // get extensions - const char *exts_str = (const char *)glGetString(GL_EXTENSIONS); - if (exts_str == NULL) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: Failed to get GL_EXTENSIONS"); - return NULL; - } - - sway_log(SWAY_INFO, "Creating swayfx GLES2 renderer"); - sway_log(SWAY_INFO, "Using %s", glGetString(GL_VERSION)); - sway_log(SWAY_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); - sway_log(SWAY_INFO, "GL renderer: %s", glGetString(GL_RENDERER)); - sway_log(SWAY_INFO, "Supported GLES2 extensions: %s", exts_str); - - // TODO: the rest of the gl checks - if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) { - renderer->exts.OES_egl_image_external = true; - load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES, - "glEGLImageTargetTexture2DOES"); - } - - // blur shaders - if (!link_blur_program(&renderer->shaders.blur1, blur1_frag_src)) { - goto error; - } - if (!link_blur_program(&renderer->shaders.blur2, blur2_frag_src)) { - goto error; - } - // effects shader - if (!link_blur_effects_program(&renderer->shaders.blur_effects, blur_effects_frag_src)) { - goto error; - } - // box shadow shader - if (!link_box_shadow_program(&renderer->shaders.box_shadow)) { - goto error; - } - // corner border shader - if (!link_corner_program(&renderer->shaders.corner)) { - goto error; - } - // quad fragment shader - if (!link_quad_program(&renderer->shaders.quad)) { - goto error; - } - // rounded quad fragment shaders - if (!link_rounded_quad_program(&renderer->shaders.rounded_quad, - SHADER_SOURCE_QUAD_ROUND)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_tl_quad, - SHADER_SOURCE_QUAD_ROUND_TOP_LEFT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_tr_quad, - SHADER_SOURCE_QUAD_ROUND_TOP_RIGHT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_bl_quad, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_LEFT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_br_quad, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_RIGHT)) { - goto error; - } - // stencil mask shader - if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) { - goto error; - } - // fragment shaders - if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) { - goto error; - } - if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) { - goto error; - } - if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) { - goto error; - } - - if (!eglMakeCurrent(wlr_egl_get_display(egl), - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); - goto error; - } - - sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully"); - return renderer; - -error: - glDeleteProgram(renderer->shaders.blur1.program); - glDeleteProgram(renderer->shaders.blur2.program); - glDeleteProgram(renderer->shaders.blur_effects.program); - glDeleteProgram(renderer->shaders.box_shadow.program); - glDeleteProgram(renderer->shaders.corner.program); - glDeleteProgram(renderer->shaders.quad.program); - glDeleteProgram(renderer->shaders.rounded_quad.program); - glDeleteProgram(renderer->shaders.rounded_bl_quad.program); - glDeleteProgram(renderer->shaders.rounded_br_quad.program); - glDeleteProgram(renderer->shaders.rounded_tl_quad.program); - glDeleteProgram(renderer->shaders.rounded_tr_quad.program); - glDeleteProgram(renderer->shaders.stencil_mask.program); - glDeleteProgram(renderer->shaders.tex_rgba.program); - glDeleteProgram(renderer->shaders.tex_rgbx.program); - glDeleteProgram(renderer->shaders.tex_ext.program); - - if (!eglMakeCurrent(wlr_egl_get_display(egl), - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL"); - } - - // TODO: more freeing? - free(renderer); - - sway_log(SWAY_ERROR, "GLES2 RENDERER: Error Initializing Shaders"); - return NULL; -} - -void fx_renderer_fini(struct fx_renderer *renderer) { - fx_framebuffer_release(&renderer->blur_buffer); - fx_framebuffer_release(&renderer->blur_saved_pixels_buffer); - fx_framebuffer_release(&renderer->effects_buffer); - fx_framebuffer_release(&renderer->effects_buffer_swapped); -} - -void fx_renderer_begin(struct fx_renderer *renderer, int width, int height) { - glViewport(0, 0, width, height); - renderer->viewport_width = width; - renderer->viewport_height = height; - - // Store the wlr FBO - renderer->wlr_buffer.fb = - wlr_gles2_renderer_get_current_fbo(renderer->wlr_output->renderer); - // Get the fx_texture - struct wlr_texture *wlr_texture = wlr_texture_from_buffer( - renderer->wlr_output->renderer, renderer->wlr_output->back_buffer); - renderer->wlr_buffer.texture = fx_texture_from_wlr_texture(wlr_texture); - wlr_texture_destroy(wlr_texture); - // Add the stencil to the wlr fbo - fx_framebuffer_add_stencil_buffer(&renderer->wlr_buffer, width, height); - - // Create the framebuffers - fx_framebuffer_update(&renderer->blur_saved_pixels_buffer, width, height); - fx_framebuffer_update(&renderer->effects_buffer, width, height); - fx_framebuffer_update(&renderer->effects_buffer_swapped, width, height); - - // Add a stencil buffer to the main buffer & bind the main buffer - fx_framebuffer_bind(&renderer->wlr_buffer); - - pixman_region32_init(&renderer->blur_padding_region); - - // refresh projection matrix - matrix_projection(renderer->projection, width, height, - WL_OUTPUT_TRANSFORM_FLIPPED_180); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -} - -void fx_renderer_end(struct fx_renderer *renderer) { - pixman_region32_fini(&renderer->blur_padding_region); -} - -void fx_renderer_clear(const float color[static 4]) { - glClearColor(color[0], color[1], color[2], color[3]); - glClearStencil(0); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -void fx_renderer_scissor(struct wlr_box *box) { - if (box) { - glScissor(box->x, box->y, box->width, box->height); - glEnable(GL_SCISSOR_TEST); - } else { - glDisable(GL_SCISSOR_TEST); - } -} - -void fx_renderer_stencil_mask_init() { - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glEnable(GL_STENCIL_TEST); - - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - // Disable writing to color buffer - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); -} - -void fx_renderer_stencil_mask_close(bool draw_inside_mask) { - // Reenable writing to color buffer - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (draw_inside_mask) { - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - return; - } - glStencilFunc(GL_NOTEQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); -} - -void fx_renderer_stencil_mask_fini() { - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glDisable(GL_STENCIL_TEST); -} - -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data) { - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(dst_box->height, dst_box->width) * 0.5; - if (deco_data.corner_radius > min_size) { - deco_data.corner_radius = min_size; - } - - struct tex_shader *shader = NULL; - - switch (fx_texture->target) { - case GL_TEXTURE_2D: - if (fx_texture->has_alpha) { - shader = &renderer->shaders.tex_rgba; - } else { - shader = &renderer->shaders.tex_rgbx; - } - break; - case GL_TEXTURE_EXTERNAL_OES: - shader = &renderer->shaders.tex_ext; - - if (!renderer->exts.OES_egl_image_external) { - sway_log(SWAY_ERROR, "Failed to render texture: " - "GL_TEXTURE_EXTERNAL_OES not supported"); - return false; - } - break; - default: - sway_log(SWAY_ERROR, "Aborting render"); - abort(); - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - wlr_matrix_transpose(gl_matrix, gl_matrix); - - // if there's no opacity or rounded corners we don't need to blend - if (!fx_texture->has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(fx_texture->target, fx_texture->id); - - glTexParameteri(fx_texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader->program); - - float* dim_color = deco_data.dim_color; - - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform1i(shader->tex, 0); - glUniform2f(shader->size, dst_box->width, dst_box->height); - glUniform2f(shader->position, dst_box->x, dst_box->y); - glUniform1f(shader->alpha, deco_data.alpha); - glUniform1f(shader->dim, deco_data.dim); - glUniform4f(shader->dim_color, dim_color[0], dim_color[1], dim_color[2], dim_color[3]); - glUniform1f(shader->has_titlebar, deco_data.has_titlebar); - glUniform1f(shader->discard_transparent, deco_data.discard_transparent); - glUniform1f(shader->saturation, deco_data.saturation); - glUniform1f(shader->radius, deco_data.corner_radius); - - const GLfloat x1 = src_box->x / fx_texture->width; - const GLfloat y1 = src_box->y / fx_texture->height; - const GLfloat x2 = (src_box->x + src_box->width) / fx_texture->width; - const GLfloat y2 = (src_box->y + src_box->height) / fx_texture->height; - const GLfloat texcoord[] = { - x2, y1, // top right - x1, y1, // top left - x2, y2, // bottom right - x1, y2, // bottom left - }; - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, texcoord); - - glEnableVertexAttribArray(shader->pos_attrib); - glEnableVertexAttribArray(shader->tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); - glDisableVertexAttribArray(shader->tex_attrib); - - glBindTexture(fx_texture->target, 0); - - return true; -} - -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *texture, - const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data) { - struct wlr_fbox src_box = { - .x = 0, - .y = 0, - .width = texture->width, - .height = texture->height, - }; - return fx_render_subtexture_with_matrix(renderer, texture, &src_box, - dst_box, matrix, deco_data); -} - -void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - float matrix[9]; - wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, projection); - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - if (color[3] == 1.0) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - struct quad_shader shader = renderer->shaders.quad; - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -void fx_render_rounded_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], int radius, - enum corner_location corner_location) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (radius > min_size) { - radius = min_size; - } - - struct rounded_quad_shader *shader = NULL; - - switch (corner_location) { - case ALL: - shader = &renderer->shaders.rounded_quad; - break; - case TOP_LEFT: - shader = &renderer->shaders.rounded_tl_quad; - break; - case TOP_RIGHT: - shader = &renderer->shaders.rounded_tr_quad; - break; - case BOTTOM_LEFT: - shader = &renderer->shaders.rounded_bl_quad; - break; - case BOTTOM_RIGHT: - shader = &renderer->shaders.rounded_br_quad; - break; - default: - sway_log(SWAY_ERROR, "Invalid Corner Location. Aborting render"); - abort(); - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - glEnable(GL_BLEND); - - glUseProgram(shader->program); - - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader->color, color[0], color[1], color[2], color[3]); - - // rounded corners - glUniform2f(shader->size, box->width, box->height); - glUniform2f(shader->position, box->x, box->y); - glUniform1f(shader->radius, radius); - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader->pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); -} - -void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], - enum corner_location corner_location, int radius, int border_thickness) { - if (border_thickness == 0 || box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (radius > min_size) { - radius = min_size; - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - if (color[3] == 1.0 && !radius) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - struct corner_shader shader = renderer->shaders.corner; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - - glUniform1f(shader.is_top_left, corner_location == TOP_LEFT); - glUniform1f(shader.is_top_right, corner_location == TOP_RIGHT); - glUniform1f(shader.is_bottom_left, corner_location == BOTTOM_LEFT); - glUniform1f(shader.is_bottom_right, corner_location == BOTTOM_RIGHT); - - glUniform2f(shader.position, box->x, box->y); - glUniform1f(shader.radius, radius); - glUniform2f(shader.half_size, box->width / 2.0, box->height / 2.0); - glUniform1f(shader.half_thickness, border_thickness / 2.0); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -void fx_render_stencil_mask(struct fx_renderer *renderer, const struct wlr_box *box, - const float matrix[static 9], int corner_radius) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (corner_radius > min_size) { - corner_radius = min_size; - } - - // TODO: just pass gl_matrix? - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - glEnable(GL_BLEND); - - struct stencil_mask_shader shader = renderer->shaders.stencil_mask; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - - glUniform2f(shader.half_size, box->width * 0.5, box->height * 0.5); - glUniform2f(shader.position, box->x, box->y); - glUniform1f(shader.radius, corner_radius); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -// TODO: alpha input arg? -void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box, - const struct wlr_box *inner_box, const float color[static 4], - const float matrix[static 9], int corner_radius, float blur_sigma) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (corner_radius > min_size) { - corner_radius = min_size; - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - fx_renderer_stencil_mask_init(); - // Draw the rounded rect as a mask - fx_render_stencil_mask(renderer, inner_box, matrix, corner_radius); - fx_renderer_stencil_mask_close(false); - - // blending will practically always be needed (unless we have a madman - // who uses opaque shadows with zero sigma), so just enable it - glEnable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - struct box_shadow_shader shader = renderer->shaders.box_shadow; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - glUniform1f(shader.blur_sigma, blur_sigma); - glUniform1f(shader.corner_radius, corner_radius); - - glUniform2f(shader.size, box->width, box->height); - glUniform2f(shader.position, box->x, box->y); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - fx_renderer_stencil_mask_fini(); -} - -void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, struct blur_shader *shader, - const struct wlr_box *box, int blur_radius) { - glDisable(GL_BLEND); - glDisable(GL_STENCIL_TEST); - - glActiveTexture(GL_TEXTURE0); - - glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); - - glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader->program); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - float gl_matrix[9]; - wlr_matrix_transpose(gl_matrix, matrix); - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - - glUniform1i(shader->tex, 0); - glUniform1f(shader->radius, blur_radius); - - if (shader == &renderer->shaders.blur1) { - glUniform2f(shader->halfpixel, 0.5f / (renderer->viewport_width / 2.0f), 0.5f / (renderer->viewport_height / 2.0f)); - } else { - glUniform2f(shader->halfpixel, 0.5f / (renderer->viewport_width * 2.0f), 0.5f / (renderer->viewport_height * 2.0f)); - } - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - - glEnableVertexAttribArray(shader->pos_attrib); - glEnableVertexAttribArray(shader->tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); - glDisableVertexAttribArray(shader->tex_attrib); - -} - -void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, - float blur_contrast, float blur_saturation) { - struct effects_shader shader = renderer->shaders.blur_effects; - - glActiveTexture(GL_TEXTURE0); - glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); - glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader.program); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - float gl_matrix[9]; - wlr_matrix_transpose(gl_matrix, matrix); - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - - glUniform1i(shader.tex, 0); - glUniform1f(shader.noise, blur_noise); - glUniform1f(shader.brightness, blur_brightness); - glUniform1f(shader.contrast, blur_contrast); - glUniform1f(shader.saturation, blur_saturation); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader.tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - glEnableVertexAttribArray(shader.tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); - glDisableVertexAttribArray(shader.tex_attrib); -} diff --git a/sway/desktop/fx_renderer/fx_stencilbuffer.c b/sway/desktop/fx_renderer/fx_stencilbuffer.c deleted file mode 100644 index 5b99ff79..00000000 --- a/sway/desktop/fx_renderer/fx_stencilbuffer.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" - -struct fx_stencilbuffer fx_stencilbuffer_create() { - return (struct fx_stencilbuffer) { - .rb = -1, - .width = -1, - .height = -1, - }; -} - -void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer) { - if (stencil_buffer->rb != (uint32_t) -1 && stencil_buffer->rb) { - glDeleteRenderbuffers(1, &stencil_buffer->rb); - } - stencil_buffer->rb = -1; - stencil_buffer->width = -1; - stencil_buffer->height = -1; -} diff --git a/sway/desktop/fx_renderer/fx_texture.c b/sway/desktop/fx_renderer/fx_texture.c deleted file mode 100644 index cc5d14c8..00000000 --- a/sway/desktop/fx_renderer/fx_texture.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_texture fx_texture_create() { - return (struct fx_texture) { - .id = 0, - .target = 0, - .width = -1, - .height = -1, - }; -} - -struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture *texture) { - assert(wlr_texture_is_gles2(texture)); - - struct wlr_gles2_texture_attribs texture_attrs; - wlr_gles2_texture_get_attribs(texture, &texture_attrs); - - return (struct fx_texture) { - .target = texture_attrs.target, - .id = texture_attrs.tex, - .has_alpha = texture_attrs.has_alpha, - .width = texture->width, - .height = texture->height, - }; -} - -void fx_texture_release(struct fx_texture *texture) { - if (texture->id) { - glDeleteTextures(1, &texture->id); - } - texture->id = 0; - texture->width = -1; - texture->height = -1; -} diff --git a/sway/desktop/fx_renderer/matrix.c b/sway/desktop/fx_renderer/matrix.c deleted file mode 100644 index 9c9dabec..00000000 --- a/sway/desktop/fx_renderer/matrix.c +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include - -#include "sway/desktop/fx_renderer/matrix.h" - -static const float transforms[][9] = { - [WL_OUTPUT_TRANSFORM_NORMAL] = { - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_90] = { - 0.0f, 1.0f, 0.0f, - -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_180] = { - -1.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_270] = { - 0.0f, -1.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_FLIPPED] = { - -1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_FLIPPED_90] = { - 0.0f, 1.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_FLIPPED_180] = { - 1.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, - [WL_OUTPUT_TRANSFORM_FLIPPED_270] = { - 0.0f, -1.0f, 0.0f, - -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, - }, -}; - -void matrix_projection(float mat[static 9], int width, int height, - enum wl_output_transform transform) { - memset(mat, 0, sizeof(*mat) * 9); - - const float *t = transforms[transform]; - float x = 2.0f / width; - float y = 2.0f / height; - - // Rotation + reflection - mat[0] = x * t[0]; - mat[1] = x * t[1]; - mat[3] = y * -t[3]; - mat[4] = y * -t[4]; - - // Translation - mat[2] = -copysign(1.0f, mat[0] + mat[1]); - mat[5] = -copysign(1.0f, mat[3] + mat[4]); - - // Identity - mat[8] = 1.0f; -} diff --git a/sway/desktop/fx_renderer/shaders/blur1.frag b/sway/desktop/fx_renderer/shaders/blur1.frag deleted file mode 100644 index e7cb1be8..00000000 --- a/sway/desktop/fx_renderer/shaders/blur1.frag +++ /dev/null @@ -1,18 +0,0 @@ -precision mediump float; -varying mediump vec2 v_texcoord; -uniform sampler2D tex; - -uniform float radius; -uniform vec2 halfpixel; - -void main() { - vec2 uv = v_texcoord * 2.0; - - vec4 sum = texture2D(tex, uv) * 4.0; - sum += texture2D(tex, uv - halfpixel.xy * radius); - sum += texture2D(tex, uv + halfpixel.xy * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); - sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - - gl_FragColor = sum / 8.0; -} diff --git a/sway/desktop/fx_renderer/shaders/blur2.frag b/sway/desktop/fx_renderer/shaders/blur2.frag deleted file mode 100644 index 3755a294..00000000 --- a/sway/desktop/fx_renderer/shaders/blur2.frag +++ /dev/null @@ -1,22 +0,0 @@ -precision mediump float; -varying mediump vec2 v_texcoord; -uniform sampler2D tex; - -uniform float radius; -uniform vec2 halfpixel; - -void main() { - vec2 uv = v_texcoord / 2.0; - - vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); - - sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); - sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; - - gl_FragColor = sum / 12.0; -} diff --git a/sway/desktop/fx_renderer/shaders/blur_effects.frag b/sway/desktop/fx_renderer/shaders/blur_effects.frag deleted file mode 100644 index 2fc16c15..00000000 --- a/sway/desktop/fx_renderer/shaders/blur_effects.frag +++ /dev/null @@ -1,54 +0,0 @@ -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -uniform float noise; -uniform float brightness; -uniform float contrast; -uniform float saturation; - -mat4 brightnessMatrix() { - float b = brightness - 1.0; - return mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - b, b, b, 1); -} - -mat4 contrastMatrix() { - float t = (1.0 - contrast) / 2.0; - return mat4(contrast, 0, 0, 0, - 0, contrast, 0, 0, - 0, 0, contrast, 0, - t, t, t, 1); -} - -mat4 saturationMatrix() { - vec3 luminance = vec3(0.3086, 0.6094, 0.0820); - float oneMinusSat = 1.0 - saturation; - vec3 red = vec3(luminance.x * oneMinusSat); - red+= vec3(saturation, 0, 0); - vec3 green = vec3(luminance.y * oneMinusSat); - green += vec3(0, saturation, 0); - vec3 blue = vec3(luminance.z * oneMinusSat); - blue += vec3(0, 0, saturation); - return mat4(red, 0, - green, 0, - blue, 0, - 0, 0, 0, 1); -} - -// Fast generative noise function -float hash(vec2 p) { - return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); -} - -void main() { - vec4 color = texture2D(tex, v_texcoord); - color *= brightnessMatrix() * contrastMatrix() * saturationMatrix(); - float noiseHash = hash(v_texcoord); - float noiseAmount = (mod(noiseHash, 1.0) - 0.5); - color.rgb += noiseAmount * noise; - - gl_FragColor = color; -} diff --git a/sway/desktop/fx_renderer/shaders/box_shadow.frag b/sway/desktop/fx_renderer/shaders/box_shadow.frag deleted file mode 100644 index c9b2b91f..00000000 --- a/sway/desktop/fx_renderer/shaders/box_shadow.frag +++ /dev/null @@ -1,74 +0,0 @@ -// Writeup: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/ - -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform vec2 position; -uniform vec2 size; -uniform float blur_sigma; -uniform float corner_radius; - -float gaussian(float x, float sigma) { - const float pi = 3.141592653589793; - return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma); -} - -// approximates the error function, needed for the gaussian integral -vec2 erf(vec2 x) { - vec2 s = sign(x), a = abs(x); - x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a; - x *= x; - return s - s / (x * x); -} - -// return the blurred mask along the x dimension -float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) { - float delta = min(halfSize.y - corner - abs(y), 0.0); - float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); - vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma)); - return integral.y - integral.x; -} - -// return the mask for the shadow of a box from lower to upper -float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner_radius) { - // Center everything to make the math easier - vec2 center = (lower + upper) * 0.5; - vec2 halfSize = (upper - lower) * 0.5; - point -= center; - - // The signal is only non-zero in a limited range, so don't waste samples - float low = point.y - halfSize.y; - float high = point.y + halfSize.y; - float start = clamp(-3.0 * sigma, low, high); - float end = clamp(3.0 * sigma, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - float step = (end - start) / 4.0; - float y = start + step * 0.5; - float value = 0.0; - for (int i = 0; i < 4; i++) { - value += roundedBoxShadowX(point.x, point.y - y, sigma, corner_radius, halfSize) * gaussian(y, sigma) * step; - y += step; - } - - return value; -} - -// per-pixel "random" number between 0 and 1 -float random() { - return fract(sin(dot(vec2(12.9898, 78.233), gl_FragCoord.xy)) * 43758.5453); -} - -void main() { - float frag_alpha = v_color.a * roundedBoxShadow( - position + blur_sigma, - position + size - blur_sigma, - gl_FragCoord.xy, blur_sigma * 0.5, - corner_radius); - - // dither the alpha to break up color bands - frag_alpha += (random() - 0.5) / 128.0; - - gl_FragColor = vec4(v_color.rgb, frag_alpha); -} diff --git a/sway/desktop/fx_renderer/shaders/common.vert b/sway/desktop/fx_renderer/shaders/common.vert deleted file mode 100644 index 811e0f2d..00000000 --- a/sway/desktop/fx_renderer/shaders/common.vert +++ /dev/null @@ -1,12 +0,0 @@ -uniform mat3 proj; -uniform vec4 color; -attribute vec2 pos; -attribute vec2 texcoord; -varying vec4 v_color; -varying vec2 v_texcoord; - -void main() { - gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); - v_color = color; - v_texcoord = texcoord; -} diff --git a/sway/desktop/fx_renderer/shaders/corner.frag b/sway/desktop/fx_renderer/shaders/corner.frag deleted file mode 100644 index 7699299a..00000000 --- a/sway/desktop/fx_renderer/shaders/corner.frag +++ /dev/null @@ -1,36 +0,0 @@ -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform bool is_top_left; -uniform bool is_top_right; -uniform bool is_bottom_left; -uniform bool is_bottom_right; - -uniform vec2 position; -uniform float radius; -uniform vec2 half_size; -uniform float half_thickness; - -float roundedBoxSDF(vec2 center, vec2 size, float radius) { - return length(max(abs(center) - size + radius, 0.0)) - radius; -} - -void main() { - vec2 center = gl_FragCoord.xy - position - half_size; - float distance = roundedBoxSDF(center, half_size - half_thickness, radius + half_thickness); - float smoothedAlphaOuter = 1.0 - smoothstep(-1.0, 1.0, distance - half_thickness); - // Create an inner circle that isn't as anti-aliased as the outer ring - float smoothedAlphaInner = 1.0 - smoothstep(-1.0, 0.5, distance + half_thickness); - gl_FragColor = mix(vec4(0), v_color, smoothedAlphaOuter - smoothedAlphaInner); - - if (is_top_left && (center.y > 0.0 || center.x > 0.0)) { - discard; - } else if (is_top_right && (center.y > 0.0 || center.x < 0.0)) { - discard; - } else if (is_bottom_left && (center.y < 0.0 || center.x > 0.0)) { - discard; - } else if (is_bottom_right && (center.y < 0.0 || center.x < 0.0)) { - discard; - } -} diff --git a/sway/desktop/fx_renderer/shaders/embed.sh b/sway/desktop/fx_renderer/shaders/embed.sh deleted file mode 100644 index 47f07892..00000000 --- a/sway/desktop/fx_renderer/shaders/embed.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -eu - -var=${1:-data} -hex="$(od -A n -t x1 -v)" - -echo "static const char $var[] = {" -for byte in $hex; do - echo " 0x$byte," -done -echo " 0x00," -echo "};" diff --git a/sway/desktop/fx_renderer/shaders/meson.build b/sway/desktop/fx_renderer/shaders/meson.build deleted file mode 100644 index 19f76dc2..00000000 --- a/sway/desktop/fx_renderer/shaders/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -embed = find_program('./embed.sh', native: true) - -shaders = [ - 'blur1.frag', - 'blur2.frag', - 'blur_effects.frag', - 'box_shadow.frag', - 'common.vert', - 'corner.frag', - 'quad.frag', - 'quad_round.frag', - 'stencil_mask.frag', - 'tex.frag', -] - -foreach name : shaders - output = name.underscorify() + '_src.h' - var = name.underscorify() + '_src' - sway_sources += custom_target( - output, - command: [embed, var], - input: name, - output: output, - feed: true, - capture: true, - ) -endforeach diff --git a/sway/desktop/fx_renderer/shaders/quad.frag b/sway/desktop/fx_renderer/shaders/quad.frag deleted file mode 100644 index 7c763272..00000000 --- a/sway/desktop/fx_renderer/shaders/quad.frag +++ /dev/null @@ -1,7 +0,0 @@ -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -void main() { - gl_FragColor = v_color; -} diff --git a/sway/desktop/fx_renderer/shaders/quad_round.frag b/sway/desktop/fx_renderer/shaders/quad_round.frag deleted file mode 100644 index 02e99028..00000000 --- a/sway/desktop/fx_renderer/shaders/quad_round.frag +++ /dev/null @@ -1,39 +0,0 @@ -#define SOURCE_QUAD_ROUND 1 -#define SOURCE_QUAD_ROUND_TOP_LEFT 2 -#define SOURCE_QUAD_ROUND_TOP_RIGHT 3 -#define SOURCE_QUAD_ROUND_BOTTOM_RIGHT 4 -#define SOURCE_QUAD_ROUND_BOTTOM_LEFT 5 - -#if !defined(SOURCE) -#error "Missing shader preamble" -#endif - -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform vec2 size; -uniform vec2 position; -uniform float radius; - -vec2 getCornerDist() { -#if SOURCE == SOURCE_QUAD_ROUND - vec2 half_size = size * 0.5; - return abs(gl_FragCoord.xy - position - half_size) - half_size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_TOP_LEFT - return abs(gl_FragCoord.xy - position - size) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_TOP_RIGHT - return abs(gl_FragCoord.xy - position - vec2(0, size.y)) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_BOTTOM_RIGHT - return abs(gl_FragCoord.xy - position) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_BOTTOM_LEFT - return abs(gl_FragCoord.xy - position - vec2(size.x, 0)) - size + radius; -#endif -} - -void main() { - vec2 q = getCornerDist(); - float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius; - float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist); - gl_FragColor = mix(vec4(0), v_color, smoothedAlpha); -} diff --git a/sway/desktop/fx_renderer/shaders/stencil_mask.frag b/sway/desktop/fx_renderer/shaders/stencil_mask.frag deleted file mode 100644 index ee033070..00000000 --- a/sway/desktop/fx_renderer/shaders/stencil_mask.frag +++ /dev/null @@ -1,17 +0,0 @@ -precision mediump float; -varying vec2 v_texcoord; - -uniform vec2 half_size; -uniform vec2 position; -uniform float radius; - -void main() { - vec2 q = abs(gl_FragCoord.xy - position - half_size) - half_size + radius; - float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius; - float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist); - gl_FragColor = mix(vec4(0.0), vec4(1.0), smoothedAlpha); - - if (gl_FragColor.a < 1.0) { - discard; - } -} diff --git a/sway/desktop/fx_renderer/shaders/tex.frag b/sway/desktop/fx_renderer/shaders/tex.frag deleted file mode 100644 index 77501887..00000000 --- a/sway/desktop/fx_renderer/shaders/tex.frag +++ /dev/null @@ -1,67 +0,0 @@ -#define SOURCE_TEXTURE_RGBA 1 -#define SOURCE_TEXTURE_RGBX 2 -#define SOURCE_TEXTURE_EXTERNAL 3 - -#if !defined(SOURCE) -#error "Missing shader preamble" -#endif - -#if SOURCE == SOURCE_TEXTURE_EXTERNAL -#extension GL_OES_EGL_image_external : require -#endif - -precision mediump float; - -varying vec2 v_texcoord; - -#if SOURCE == SOURCE_TEXTURE_EXTERNAL -uniform samplerExternalOES tex; -#elif SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_RGBX -uniform sampler2D tex; -#endif - -uniform float alpha; -uniform float dim; -uniform vec4 dim_color; -uniform vec2 size; -uniform vec2 position; -uniform float radius; -uniform float saturation; -uniform bool has_titlebar; -uniform bool discard_transparent; - -const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721); - -vec4 sample_texture() { -#if SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_EXTERNAL - return texture2D(tex, v_texcoord); -#elif SOURCE == SOURCE_TEXTURE_RGBX - return vec4(texture2D(tex, v_texcoord).rgb, 1.0); -#endif -} - -void main() { - vec4 color = sample_texture(); - // Saturation - if (saturation != 1.0) { - vec4 pixColor = texture2D(tex, v_texcoord); - vec3 irgb = pixColor.rgb; - vec3 target = vec3(dot(irgb, saturation_weight)); - color = vec4(mix(target, irgb, saturation), pixColor.a); - } - // Dimming - gl_FragColor = mix(color, dim_color, dim) * alpha; - - if (!has_titlebar || gl_FragCoord.y - position.y > radius) { - vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy); - if (max(corner_distance.x, corner_distance.y) < radius) { - float d = radius - distance(corner_distance, vec2(radius)); - float smooth = smoothstep(-1.0, 0.5, d); - gl_FragColor = mix(vec4(0), gl_FragColor, smooth); - } - } - - if (discard_transparent && gl_FragColor.a == 0.0) { - discard; - } -} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 3a4d0b87..f3af7aa1 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,4 @@ #include -#include #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" @@ -12,7 +11,7 @@ static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->destroy.link); - sway_idle_inhibit_v1_check_active(inhibitor->manager); + sway_idle_inhibit_v1_check_active(); free(inhibitor); } @@ -35,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { return; } - inhibitor->manager = manager; inhibitor->mode = INHIBIT_IDLE_APPLICATION; inhibitor->wlr_inhibitor = wlr_inhibitor; wl_list_insert(&manager->inhibitors, &inhibitor->link); @@ -43,33 +41,34 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - sway_idle_inhibit_v1_check_active(manager); + sway_idle_inhibit_v1_check_active(); } void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; + struct sway_idle_inhibitor_v1 *inhibitor = calloc(1, sizeof(struct sway_idle_inhibitor_v1)); if (!inhibitor) { return; } - inhibitor->manager = server.idle_inhibit_manager_v1; inhibitor->mode = mode; inhibitor->view = view; - wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); + wl_list_insert(&manager->inhibitors, &inhibitor->link); inhibitor->destroy.notify = handle_destroy; wl_signal_add(&view->events.unmap, &inhibitor->destroy); - sway_idle_inhibit_v1_check_active(inhibitor->manager); + sway_idle_inhibit_v1_check_active(); } struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( struct sway_view *view) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, - link) { + wl_list_for_each(inhibitor, &manager->inhibitors, link) { if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && inhibitor->view == view) { return inhibitor; @@ -80,9 +79,9 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( struct sway_view *view) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, - link) { + wl_list_for_each(inhibitor, &manager->inhibitors, link) { if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { return inhibitor; @@ -131,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { return false; } -void sway_idle_inhibit_v1_check_active( - struct sway_idle_inhibit_manager_v1 *manager) { +void sway_idle_inhibit_v1_check_active(void) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; wl_list_for_each(inhibitor, &manager->inhibitors, link) { @@ -140,28 +139,21 @@ void sway_idle_inhibit_v1_check_active( break; } } - wlr_idle_set_enabled(manager->idle, NULL, !inhibited); wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); } -struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( - struct wl_display *wl_display, struct wlr_idle *idle) { - struct sway_idle_inhibit_manager_v1 *manager = - calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); - if (!manager) { - return NULL; +bool sway_idle_inhibit_manager_v1_init(void) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; + + manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); + if (!manager->wlr_manager) { + return false; } - manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); - if (!manager->wlr_manager) { - free(manager); - return NULL; - } - manager->idle = idle; wl_signal_add(&manager->wlr_manager->events.new_inhibitor, &manager->new_idle_inhibitor_v1); manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; wl_list_init(&manager->inhibitors); - return manager; + return true; } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 48e5d24c..0c02b997 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -67,9 +67,12 @@ void launcher_ctx_destroy(struct launcher_ctx *ctx) { } wl_list_remove(&ctx->node_destroy.link); wl_list_remove(&ctx->token_destroy.link); + if (ctx->seat) { + wl_list_remove(&ctx->seat_destroy.link); + } wl_list_remove(&ctx->link); wlr_xdg_activation_token_v1_destroy(ctx->token); - free(ctx->name); + free(ctx->fallback_name); free(ctx); } @@ -114,22 +117,22 @@ struct sway_workspace *launcher_ctx_get_workspace( break; case N_OUTPUT: output = ctx->node->sway_output; - ws = workspace_by_name(ctx->name); + ws = workspace_by_name(ctx->fallback_name); if (!ws) { sway_log(SWAY_DEBUG, "Creating workspace %s for pid %d because it disappeared", - ctx->name, ctx->pid); + ctx->fallback_name, ctx->pid); if (!output->enabled) { sway_log(SWAY_DEBUG, "Workspace output %s is disabled, trying another one", output->wlr_output->name); output = NULL; } - ws = workspace_create(output, ctx->name); + ws = workspace_create(output, ctx->fallback_name); } break; case N_ROOT: - ws = workspace_create(NULL, ctx->name); + ws = workspace_create(NULL, ctx->fallback_name); break; } @@ -148,8 +151,8 @@ static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { wl_list_init(&ctx->node_destroy.link); // We want to save this ws name to recreate later, hopefully on the // same output - free(ctx->name); - ctx->name = strdup(ws->name); + free(ctx->fallback_name); + ctx->fallback_name = strdup(ws->name); if (!ws->output || ws->output->node.destroying) { // If the output is being destroyed it would be pointless to track // If the output is being disabled, we'll find out if it's still @@ -178,21 +181,43 @@ static void token_handle_destroy(struct wl_listener *listener, void *data) { launcher_ctx_destroy(ctx); } -struct launcher_ctx *launcher_ctx_create() { - struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); +struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, + struct sway_node *node) { + struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); + + const char *fallback_name = NULL; + struct sway_workspace *ws = NULL; + switch (node->type) { + case N_CONTAINER: + // Unimplemented + free(ctx); + return NULL; + case N_WORKSPACE: + ws = node->sway_workspace; + fallback_name = ws->name; + break; + case N_OUTPUT:; + struct sway_output *output = node->sway_output; + ws = output_get_active_workspace(output); + fallback_name = ws ? ws->name : NULL; + break; + case N_ROOT: + // Unimplemented + free(ctx); return NULL; } - struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); - struct wlr_xdg_activation_token_v1 *token = - wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); - token->data = ctx; - ctx->name = strdup(ws->name); + if (!fallback_name) { + // TODO: implement a better fallback. + free(ctx); + return NULL; + } + + ctx->fallback_name = strdup(fallback_name); ctx->token = token; - ctx->node = &ws->node; + ctx->node = node; + // Having surface set means that the focus check in wlroots has passed + ctx->had_focused_surface = token->surface != NULL; ctx->node_destroy.notify = ctx_handle_node_destroy; wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); @@ -202,6 +227,38 @@ struct launcher_ctx *launcher_ctx_create() { wl_list_init(&ctx->link); wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); + + token->data = ctx; + return ctx; +} + +static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); + ctx->seat = NULL; + wl_list_remove(&ctx->seat_destroy.link); +} + +// Creates a context with a new token for the internal launcher +struct launcher_ctx *launcher_ctx_create_internal(void) { + struct sway_seat *seat = input_manager_current_seat(); + struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace."); + return NULL; + } + + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); + + struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); + if (!ctx) { + wlr_xdg_activation_token_v1_destroy(token); + return NULL; + } + ctx->seat = seat; + ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; + wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); + return ctx; } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d0d56755..81429053 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,10 +15,44 @@ #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) { + struct wlr_layer_surface_v1 *layer; + do { + if (!surface) { + return NULL; + } + // Topmost layer surface + if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { + return layer; + } + // Layer subsurface + if (wlr_subsurface_try_from_wlr_surface(surface)) { + surface = wlr_surface_get_root_surface(surface); + continue; + } + + // Layer surface popup + struct wlr_xdg_surface *xdg_surface = NULL; + if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && + xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { + if (!xdg_surface->popup->parent) { + return NULL; + } + surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); + continue; + } + + // Return early if the surface is not a layer/xdg_popup/sub surface + return NULL; + } 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) { @@ -233,8 +268,9 @@ void arrange_layers(struct sway_output *output) { 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 && - layer->layer_surface->mapped) { + if (layer->layer_surface->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + layer->layer_surface->surface->mapped) { topmost = layer; break; } @@ -246,10 +282,12 @@ void arrange_layers(struct sway_output *output) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { + seat->has_exclusive_layer = false; if (topmost != NULL) { seat_set_focus_layer(seat, topmost->layer_surface); } else if (seat->focused_layer && - !seat->focused_layer->current.keyboard_interactive) { + seat->focused_layer->current.keyboard_interactive + != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { seat_set_focus_layer(seat, NULL); } } @@ -268,7 +306,7 @@ static struct sway_layer_surface *find_mapped_layer_by_client( &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { struct wl_resource *resource = lsurface->layer_surface->resource; if (wl_resource_get_client(resource) == client - && lsurface->layer_surface->mapped) { + && lsurface->layer_surface->surface->mapped) { return lsurface; } } @@ -309,13 +347,15 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { // Rerender the static blur on change if (layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - output->renderer->blur_buffer_dirty = true; + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; } bool layer_changed = false; if (layer_surface->current.committed != 0 - || layer->mapped != layer_surface->mapped) { - layer->mapped = layer_surface->mapped; + || 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); @@ -376,7 +416,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { 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->mapped) { + if (sway_layer->layer_surface->surface->mapped) { unmap(sway_layer); } @@ -400,7 +440,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { // Rerender the static blur if (sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - output->renderer->blur_buffer_dirty = true; + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; } arrange_layers(output); @@ -420,8 +462,6 @@ static void handle_map(struct wl_listener *listener, void *data) { layer_parse_criteria(sway_layer); output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); - wlr_surface_send_enter(sway_layer->layer_surface->surface, - sway_layer->layer_surface->output); cursor_rebase_all(); } @@ -491,9 +531,9 @@ static struct sway_layer_subsurface *create_subsurface( wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); + wl_signal_add(&wlr_subsurface->surface->events.map, &subsurface->map); subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->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; @@ -548,7 +588,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) { 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"); - wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); + surface_enter_output(popup->wlr_popup->base->surface, wlr_output->data); popup_damage(popup, true); } @@ -608,9 +648,9 @@ static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, popup->parent_layer = parent; popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->events.map, &popup->map); + wl_signal_add(&wlr_popup->base->surface->events.map, &popup->map); popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->events.unmap, &popup->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; @@ -698,9 +738,9 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 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->events.map, &sway_layer->map); + wl_signal_add(&layer_surface->surface->events.map, &sway_layer->map); sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->events.unmap, &sway_layer->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; @@ -722,6 +762,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { 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; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ca883144..2ccb28f2 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1,15 +1,18 @@ #define _POSIX_C_SOURCE 200809L #include +#include +#include #include #include #include #include -#include +#include #include +#include #include #include #include -#include +#include #include #include #include @@ -18,10 +21,12 @@ #include #include "config.h" #include "log.h" +#include "scenefx/render/pass.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/layers.h" #include "sway/output.h" #include "sway/server.h" @@ -32,13 +37,27 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#if WLR_HAS_DRM_BACKEND +#include +#include +#endif + +bool output_match_name_or_id(struct sway_output *output, + const char *name_or_id) { + if (strcmp(name_or_id, "*") == 0) { + return true; + } + + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + return strcasecmp(identifier, name_or_id) == 0 + || strcasecmp(output->wlr_output->name, name_or_id) == 0; +} + struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -48,10 +67,7 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { struct sway_output *all_output_by_name_or_id(const char *name_or_id) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -262,7 +278,7 @@ void output_drag_icons_for_each_surface(struct sway_output *output, double ox = drag_icon->x - output->lx; double oy = drag_icon->y - output->ly; - if (drag_icon->wlr_drag_icon->mapped) { + 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); @@ -292,7 +308,7 @@ static void output_for_each_surface(struct sway_output *output, if (lock_surface->output != output->wlr_output) { continue; } - if (!lock_surface->mapped) { + if (!lock_surface->surface->mapped) { continue; } @@ -369,14 +385,14 @@ overlay: } static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * 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 = round(box->x * scale); - box->y = round(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) { @@ -454,7 +470,7 @@ static void count_surface_iterator(struct sway_output *output, } static bool scan_out_fullscreen_view(struct sway_output *output, - struct sway_view *view) { + 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")) { @@ -500,6 +516,12 @@ static bool scan_out_fullscreen_view(struct sway_output *output, 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; @@ -510,24 +532,56 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } - wlr_output_attach_buffer(wlr_output, &surface->buffer->base); - if (!wlr_output_test(wlr_output)) { + if (!wlr_output_is_direct_scanout_allowed(wlr_output)) { return false; } - wlr_presentation_surface_sampled_on_output(server.presentation, surface, + 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(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; - if (output->wlr_output == NULL) { + struct wlr_output *wlr_output = output->wlr_output; + if (wlr_output == NULL) { return 0; } - output->wlr_output->frame_pending = false; + wlr_output->frame_pending = false; + + if (!wlr_output->needs_frame && + !output->gamma_lut_changed && + !pixman_region32_not_empty(&output->damage_ring.current)) { + return 0; + } struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -539,13 +593,33 @@ static int output_repaint_timer_handler(void *data) { fullscreen_con = workspace->current.fullscreen; } + struct wlr_output_state pending = {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); + 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}; + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + } + } + + pending.committed |= WLR_OUTPUT_STATE_DAMAGE; + get_frame_damage(output, &pending.damage); + 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, fullscreen_con->view); + 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", @@ -559,32 +633,72 @@ static int output_repaint_timer_handler(void *data) { last_scanned_out = scanned_out; if (scanned_out) { - return 0; + goto out; } } + if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) { + goto out; + } + int buffer_age; - if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { - return 0; + 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 (!output->wlr_output->needs_frame && - !pixman_region32_not_empty(&output->damage_ring.current)) { - pixman_region32_fini(&damage); - wlr_output_rollback(output->wlr_output); - return 0; + + 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(output, &now, &damage); + 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; } @@ -610,9 +724,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { if (output->max_render_time != 0) { struct timespec now; - clockid_t presentation_clock - = wlr_backend_get_presentation_clock(server.backend); - clock_gettime(presentation_clock, &now); + clock_gettime(CLOCK_MONOTONIC, &now); const long NSEC_IN_SECONDS = 1000000000; struct timespec predicted_refresh = output->last_presentation; @@ -690,14 +802,13 @@ static void damage_surface_iterator(struct sway_output *output, pixman_region32_init(&damage); wlr_surface_get_effective_damage(surface, &damage); wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current.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, - ceil(output->wlr_output->scale) - surface->current.scale); + 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); } @@ -754,19 +865,33 @@ static void damage_child_views_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { - int shadow_sigma = con->shadow_enabled ? config->shadow_blur_sigma : 0; - // 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 - 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, + .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); @@ -790,38 +915,35 @@ static void update_output_manager_config(struct sway_server *server) { wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box); // We mark the output enabled when it's switched off but not disabled - config_head->state.enabled = output->current_mode != NULL && output->enabled; - config_head->state.mode = output->current_mode; - if (!wlr_box_empty(&output_box)) { - config_head->state.x = output_box.x; - config_head->state.y = output_box.y; - } + config_head->state.enabled = !wlr_box_empty(&output_box); + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); + + ipc_event_output(); } -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, destroy); +static void begin_destroy(struct sway_output *output) { struct sway_server *server = output->server; if (output->enabled) { output_disable(output); } - fx_renderer_fini(output->renderer); - output_begin_destroy(output); wl_list_remove(&output->link); + wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); - wl_list_remove(&output->mode.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); @@ -833,35 +955,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { update_output_manager_config(server); } -static void handle_mode(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, mode); - if (!output->enabled && !output->enabling) { - struct output_config *oc = find_output_config(output); - if (output->wlr_output->current_mode != NULL && - (!oc || oc->enabled)) { - // We want to enable this output, but it didn't work last time, - // possibly because we hadn't enough CRTCs. Try again now that the - // output has a mode. - sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " - "trying to enable it", output->wlr_output->name); - apply_output_config(oc, output); - } - return; - } - if (!output->enabled) { - return; - } +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, destroy); + begin_destroy(output); +} - arrange_layers(output); - arrange_output(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); - wlr_output_schedule_frame(output->wlr_output); - - update_output_manager_config(output->server); +static void handle_layout_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, layout_destroy); + begin_destroy(output); } static void update_textures(struct sway_container *con, void *data) { @@ -869,6 +970,12 @@ static void update_textures(struct sway_container *con, void *data) { 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 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; @@ -877,11 +984,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->committed & WLR_OUTPUT_STATE_SCALE) { + 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); } - if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { + 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(); @@ -889,12 +1005,19 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } - if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) { + 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; + } } static void handle_present(struct wl_listener *listener, void *data) { @@ -909,6 +1032,13 @@ static void handle_present(struct wl_listener *listener, void *data) { output->refresh_nsec = output_event->refresh; } +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); +} + static unsigned int last_headless_num = 0; void handle_new_output(struct wl_listener *listener, void *data) { @@ -931,16 +1061,18 @@ void handle_new_output(struct wl_listener *listener, void *data) { if (wlr_output->non_desktop) { sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output); +#if WLR_HAS_DRM_BACKEND if (server->drm_lease_manager) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } +#endif list_add(root->non_desktop_outputs, non_desktop); return; } if (!wlr_output_init_render(wlr_output, server->allocator, - server->wlr_renderer)) { + server->renderer)) { sway_log(SWAY_ERROR, "Failed to init output render"); return; } @@ -952,20 +1084,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->server = server; wlr_damage_ring_init(&output->damage_ring); - // Init FX Renderer - struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); - output->renderer = fx_renderer_create(egl, wlr_output); - if (!output->renderer) { - sway_log(SWAY_ERROR, "Failed to create fx_renderer"); - abort(); - } - + wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); + output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; wl_signal_add(&wlr_output->events.commit, &output->commit); output->commit.notify = handle_commit; - wl_signal_add(&wlr_output->events.mode, &output->mode); - output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; wl_signal_add(&wlr_output->events.damage, &output->damage); @@ -974,6 +1098,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { 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); @@ -997,6 +1123,21 @@ void handle_output_layout_change(struct wl_listener *listener, update_output_manager_config(server); } +void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, gamma_control_set_gamma); + const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + + struct sway_output *output = event->output->data; + + if(!output) { + return; + } + + output->gamma_lut_changed = true; + 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 diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 429924c2..cbf99177 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1,28 +1,25 @@ -#include -#include -#include +#define _POSIX_C_SOURCE 200809L +#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 "sway/config.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_renderer.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" @@ -31,6 +28,56 @@ #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, @@ -45,33 +92,6 @@ struct decoration_data get_undecorated_decoration_data() { }; } -// TODO: Remove this ugly abomination with a complete border rework... -enum corner_location get_rotated_corner(enum corner_location corner_location, - enum wl_output_transform transform) { - if (corner_location == ALL || corner_location == NONE) { - return corner_location; - } - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - return corner_location; - case WL_OUTPUT_TRANSFORM_90: - return (corner_location + 1) % 4; - case WL_OUTPUT_TRANSFORM_180: - return (corner_location + 2) % 4; - case WL_OUTPUT_TRANSFORM_270: - return (corner_location + 3) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED: - return (corner_location + (1 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - return (corner_location + (4 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - return (corner_location + (3 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - return (corner_location + (2 - 2 * (corner_location % 2))) % 4; - } - return corner_location; -} - /** * Apply scale to a width or height. * @@ -85,380 +105,260 @@ enum corner_location get_rotated_corner(enum corner_location corner_location, * scaled to 2px. */ static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); + return roundf((offset + length) * scale) - roundf(offset * scale); } -static void scissor_output(struct wlr_output *wlr_output, - pixman_box32_t *rect) { - struct sway_output *output = wlr_output->data; - struct fx_renderer *renderer = output->renderer; - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(wlr_output, &ow, &oh); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - fx_renderer_scissor(&box); -} - -static void set_scale_filter(struct wlr_output *wlr_output, - struct fx_texture *texture, enum scale_filter_mode scale_filter) { - glBindTexture(texture->target, texture->id); - - switch (scale_filter) { +static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { + switch (output->scale_filter) { case SCALE_FILTER_LINEAR: - glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; + return WLR_SCALE_FILTER_BILINEAR; case SCALE_FILTER_NEAREST: - glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - assert(false); // unreachable + return WLR_SCALE_FILTER_NEAREST; + default: + abort(); // unreachable } } -pixman_region32_t create_damage(const struct wlr_box damage_box, pixman_region32_t *output_damage) { +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(&damage); - pixman_region32_union_rect(&damage, &damage, damage_box.x, damage_box.y, - damage_box.width, damage_box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - return 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 get_monitor_box(struct wlr_output *output) { - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - struct wlr_box monitor_box = { 0, 0, width, height }; - return monitor_box; -} + 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); -static void render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct fx_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], struct decoration_data deco_data) { - struct sway_output *output = wlr_output->data; - struct fx_renderer *renderer = output->renderer; + clip_box = *_clip_box; + } - pixman_region32_t damage = create_damage(*dst_box, output_damage); bool damaged = pixman_region32_not_empty(&damage); if (!damaged) { goto damage_finish; } - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, dst_box, - wlr_output_transform_invert(wlr_output->transform), width, height); + 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); - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - set_scale_filter(wlr_output, texture, output->scale_filter); - if (src_box != NULL) { - fx_render_subtexture_with_matrix(renderer, texture, src_box, &transformed_box, - matrix, deco_data); - } else { - fx_render_texture_with_matrix(renderer, texture, &transformed_box, matrix, deco_data); + 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); } -/* Renders the blur for each damaged rect and swaps the buffer */ -void render_blur_segments(struct fx_renderer *renderer, - const float matrix[static 9], pixman_region32_t* damage, - struct fx_framebuffer **buffer, struct blur_shader* shader, - const struct wlr_box *box, int blur_radius) { - if (*buffer == &renderer->effects_buffer) { - fx_framebuffer_bind(&renderer->effects_buffer_swapped); - } else { - fx_framebuffer_bind(&renderer->effects_buffer); - } +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; - if (pixman_region32_not_empty(damage)) { - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - const pixman_box32_t box = rects[i]; - struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; - fx_renderer_scissor(&new_box); - fx_render_blur(renderer, matrix, buffer, shader, &new_box, blur_radius); - } - } - - if (*buffer != &renderer->effects_buffer) { - *buffer = &renderer->effects_buffer; - } else { - *buffer = &renderer->effects_buffer_swapped; - } -} - -// Blurs the main_buffer content and returns the blurred framebuffer -struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct sway_output *output, - pixman_region32_t *original_damage, const struct wlr_box *box) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_box monitor_box = get_monitor_box(wlr_output); - - const enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0, wlr_output->transform_matrix); - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); + struct wlr_box proj_box = *dst_box; pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, original_damage); - wlr_region_transform(&damage, &damage, transform, monitor_box.width, monitor_box.height); - - wlr_region_expand(&damage, &damage, config_get_blur_size()); - - // Initially blur main_buffer content into the effects_buffers - struct fx_framebuffer *current_buffer = &renderer->wlr_buffer; - - // Bind to blur framebuffer - fx_framebuffer_bind(&renderer->effects_buffer); - glBindTexture(renderer->wlr_buffer.texture.target, renderer->wlr_buffer.texture.id); - - // damage region will be scaled, make a temp - pixman_region32_t tempDamage; - pixman_region32_init(&tempDamage); - - int blur_radius = config->blur_params.radius; - int blur_passes = config->blur_params.num_passes; - - // Downscale - for (int i = 0; i < blur_passes; ++i) { - wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << (i + 1))); - render_blur_segments(renderer, gl_matrix, &tempDamage, ¤t_buffer, - &renderer->shaders.blur1, box, blur_radius); - } - - // Upscale - for (int i = blur_passes - 1; i >= 0; --i) { - // when upsampling we make the region twice as big - wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << i)); - render_blur_segments(renderer, gl_matrix, &tempDamage, ¤t_buffer, - &renderer->shaders.blur2, box, blur_radius); - } - - float blur_noise = config->blur_params.noise; - float blur_brightness = config->blur_params.brightness; - float blur_contrast = config->blur_params.contrast; - float blur_saturation = config->blur_params.saturation; - - // Render additional blur effects like saturation, noise, contrast, etc... - if (config_should_parameters_blur_effects() && pixman_region32_not_empty(&damage)) { - if (current_buffer == &renderer->effects_buffer) { - fx_framebuffer_bind(&renderer->effects_buffer_swapped); - } else { - fx_framebuffer_bind(&renderer->effects_buffer); - } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - const pixman_box32_t box = rects[i]; - struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; - fx_renderer_scissor(&new_box); - fx_render_blur_effects(renderer, gl_matrix, ¤t_buffer, blur_noise, - blur_brightness, blur_contrast, blur_saturation); - } - if (current_buffer != &renderer->effects_buffer) { - current_buffer = &renderer->effects_buffer; - } else { - current_buffer = &renderer->effects_buffer_swapped; - } - } - - pixman_region32_fini(&tempDamage); - pixman_region32_fini(&damage); - - // Bind back to the default buffer - fx_framebuffer_bind(&renderer->wlr_buffer); - - return current_buffer; -} - -void render_blur(bool optimized, struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *dst_box, - pixman_region32_t *opaque_region, struct decoration_data *deco_data, - struct blur_stencil_data *stencil_data) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - // Check if damage is inside of box rect - pixman_region32_t damage = create_damage(*dst_box, output_damage); - - pixman_region32_t translucent_region; - pixman_region32_init(&translucent_region); + 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; } - // Gets the translucent region - pixman_box32_t surface_box = { 0, 0, dst_box->width, dst_box->height }; - pixman_region32_copy(&translucent_region, opaque_region); - pixman_region32_inverse(&translucent_region, &translucent_region, &surface_box); - if (!pixman_region32_not_empty(&translucent_region)) { - goto damage_finish; - } + transform_output_box(&proj_box, output->wlr_output); + transform_output_damage(&damage, output->wlr_output); - struct fx_framebuffer *buffer = &renderer->blur_buffer; - if (!buffer->texture.id || !optimized) { - pixman_region32_translate(&translucent_region, dst_box->x, dst_box->y); - pixman_region32_intersect(&translucent_region, &translucent_region, &damage); - - // Render the blur into its own buffer - buffer = get_main_buffer_blur(renderer, output, &translucent_region, dst_box); - } - - // Get a stencil of the window ignoring transparent regions - if (deco_data->discard_transparent && stencil_data) { - fx_renderer_scissor(NULL); - fx_renderer_stencil_mask_init(); - - render_texture(wlr_output, output_damage, stencil_data->stencil_texture, stencil_data->stencil_src_box, - dst_box, stencil_data->stencil_matrix, *deco_data); - - fx_renderer_stencil_mask_close(true); - } - - // Draw the blurred texture - struct wlr_box monitor_box = get_monitor_box(wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix); - - struct decoration_data blur_deco_data = get_undecorated_decoration_data(); - blur_deco_data.corner_radius = deco_data->corner_radius; - blur_deco_data.has_titlebar = deco_data->has_titlebar; - render_texture(wlr_output, &damage, &buffer->texture, NULL, dst_box, matrix, blur_deco_data); - - // Finish stenciling - if (deco_data->discard_transparent && stencil_data) { - fx_renderer_stencil_mask_fini(); - } + 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, + .alpha = &deco_data.alpha, + .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); - pixman_region32_fini(&translucent_region); } // _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 sway_output *output, pixman_region32_t *output_damage, - 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 = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +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)); - box.x -= blur_sigma - offset_x; - box.y -= blur_sigma - offset_y; - box.width += 2 * blur_sigma; - box.height += 2 * blur_sigma; - pixman_region32_t damage = create_damage(box, output_damage); + // 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; - // don't damage area behind window since we dont render it anyway - struct wlr_box inner_box; - memcpy(&inner_box, _box, sizeof(struct wlr_box)); - inner_box.x += corner_radius; - inner_box.y += corner_radius; - inner_box.width -= 2 * corner_radius; - inner_box.height -= 2 * corner_radius; - pixman_region32_t inner_damage = create_damage(inner_box, output_damage); - pixman_region32_subtract(&damage, &damage, &inner_damage); - pixman_region32_fini(&inner_damage); - - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { + 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; } - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + transform_output_box(&shadow_box, wlr_output); - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - // ensure the shadow_box is updated as per the output orientation - struct wlr_box transformed_shadow_box; - wlr_box_transform(&transformed_shadow_box, &box, transform, width, height); - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - wlr_box_transform(&transformed_box, _box, transform, width, height); + 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); - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); +damage_finish: + pixman_region32_fini(&damage); +} - fx_render_box_shadow(renderer, &transformed_shadow_box, &transformed_box, - color, matrix, corner_radius, blur_sigma); +// _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 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; - pixman_region32_t *output_damage = data->damage; struct wlr_texture *texture = wlr_surface_get_texture(surface); if (!texture) { return; } - struct wlr_box proj_box = *_box; - - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, wlr_output->transform_matrix); + 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 = data->clip_box; - if (clip_box != NULL) { - dst_box.width = fmin(dst_box.width, clip_box->width); - dst_box.height = fmin(dst_box.height, clip_box->height); - dst_box.x = fmax(dst_box.x, clip_box->x); - dst_box.y = fmax(dst_box.y, clip_box->y); - } + 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; - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture); - // 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; @@ -474,13 +374,11 @@ static void render_surface_iterator(struct sway_output *output, } if (has_alpha) { - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height); - struct blur_stencil_data stencil_data = { &fx_texture, &src_box, matrix }; - bool should_optimize_blur = view ? !container_is_floating_or_child(view->container) || config->blur_xray : false; - render_blur(should_optimize_blur, output, output_damage, &dst_box, - &opaque_region, &deco_data, &stencil_data); + 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); @@ -488,11 +386,10 @@ static void render_surface_iterator(struct sway_output *output, deco_data.discard_transparent = false; - // Render surface texture - render_texture(wlr_output, output_damage, &fx_texture, &src_box, &dst_box, - matrix, deco_data); + render_texture(data->ctx, texture, + &src_box, &dst_box, &clip_box, surface->current.transform, deco_data); - wlr_presentation_surface_sampled_on_output(server.presentation, surface, + wlr_presentation_surface_textured_on_output(server.presentation, surface, wlr_output); } @@ -500,225 +397,144 @@ static void render_surface_iterator(struct sway_output *output, 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_surface_is_layer_surface(surface)) { + if (!wlr_layer_surface_v1_try_from_wlr_surface(surface)) { deco_data = get_undecorated_decoration_data(); } - // render the layer's surface - render_surface_iterator(output, view, surface, _box, _data); - // render shadow if (deco_data.shadow && config_should_parameters_shadow()) { - int corner_radius = deco_data.corner_radius *= output->wlr_output->scale; - int offset_x = config->shadow_offset_x * output->wlr_output->scale; - int offset_y = config->shadow_offset_y * output->wlr_output->scale; - scale_box(_box, output->wlr_output->scale); - render_box_shadow(output, data->damage, _box, config->shadow_color, - config->shadow_blur_sigma, corner_radius, offset_x, offset_y); + 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 sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { +static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_layer_for_each_toplevel_surface(output, layer_surfaces, + output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces, render_layer_iterator, &data); } -static void render_layer_popups(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { + static void render_layer_popups(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_layer_for_each_popup_surface(output, layer_surfaces, + output_layer_for_each_popup_surface(ctx->output, layer_surfaces, render_layer_iterator, &data); } #if HAVE_XWAYLAND -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { +static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unmanaged) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_unmanaged_for_each_surface(output, unmanaged, + output_unmanaged_for_each_surface(ctx->output, unmanaged, render_surface_iterator, &data); } #endif -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { +static void render_drag_icons(struct fx_render_context *ctx, struct wl_list *drag_icons) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_drag_icons_for_each_surface(output, drag_icons, + output_drag_icons_for_each_surface(ctx->output, drag_icons, render_surface_iterator, &data); } -void render_whole_output(struct fx_renderer *renderer, struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct fx_texture *texture) { - struct wlr_box monitor_box = get_monitor_box(wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix); - - render_texture(wlr_output, output_damage, texture, NULL, &monitor_box, matrix, get_undecorated_decoration_data()); -} - -void render_output_blur(struct sway_output *output, pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - struct wlr_box monitor_box = get_monitor_box(wlr_output); - pixman_region32_t fake_damage; - pixman_region32_init_rect(&fake_damage, 0, 0, monitor_box.width, monitor_box.height); - - // Render the blur - struct fx_framebuffer *buffer = get_main_buffer_blur(renderer, output, &fake_damage, &monitor_box); - - // Render the newly blurred content into the blur_buffer - fx_framebuffer_update(&renderer->blur_buffer, - output->renderer->viewport_width, output->renderer->viewport_height); - fx_framebuffer_bind(&renderer->blur_buffer); - - // Clear the damaged region of the blur_buffer - float clear_color[] = { 0, 0, 0, 0 }; - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_renderer_clear(clear_color); - } - render_whole_output(renderer, wlr_output, &fake_damage, &buffer->texture); - fx_framebuffer_bind(&renderer->wlr_buffer); - - pixman_region32_fini(&fake_damage); - - renderer->blur_buffer_dirty = false; -} - // _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 sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, +void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, float color[static 4]) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; + struct wlr_output *wlr_output = ctx->output->wlr_output; - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; + 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 = create_damage(box, output_damage); + 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; } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_render_rect(renderer, &box, color, wlr_output->transform_matrix); - } + 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 sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, float color[static 4], int corner_radius, - enum corner_location corner_location) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +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_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; + struct wlr_output *wlr_output = ctx->output->wlr_output; - pixman_region32_t damage = create_damage(box, output_damage); + 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; } - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + transform_corner_location(&corner_location, wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, &box, transform, width, height); - - corner_location = get_rotated_corner(corner_location, transform); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_render_rounded_rect(renderer, &transformed_box, color, matrix, - corner_radius, corner_location); - } - -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_border_corner(struct sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, const float color[static 4], int corner_radius, - int border_thickness, enum corner_location corner_location) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; - - pixman_region32_t damage = create_damage(box, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, &box, transform, width, height); - - corner_location = get_rotated_corner(corner_location, transform); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_render_border_corner(renderer, &transformed_box, color, matrix, - corner_location, corner_radius, border_thickness); - } + 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); @@ -731,17 +547,18 @@ void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } -static void render_view_toplevels(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_data) { +static void render_view_toplevels(struct fx_render_context *ctx, + struct sway_view *view, struct decoration_data deco_data) { struct render_data data = { - .damage = damage, .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 = state.x - output->lx; - clip_box.y = state.y - output->ly; + 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; @@ -768,27 +585,36 @@ static void render_view_toplevels(struct sway_view *view, struct sway_output *ou } data.clip_box = &clip_box; - output_view_for_each_surface(output, view, render_surface_iterator, &data); + // 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 sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_data) { +static void render_view_popups(struct fx_render_context *ctx, struct sway_view *view, + struct decoration_data deco_data) { struct render_data data = { - .damage = damage, .deco_data = deco_data, + .view = view, + .ctx = ctx, }; - output_view_for_each_popup_surface(output, view, + output_view_for_each_popup_surface(ctx->output, view, render_surface_iterator, &data); } -static void render_saved_view(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_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) { @@ -814,55 +640,46 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output } struct wlr_box dst_box = proj_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0, wlr_output->transform_matrix); + struct wlr_box clip_box = proj_box; + // Clip to actual geometry, clipping the CSD struct sway_container_state state = view->container->current; - dst_box.x = state.x - output->lx; - dst_box.y = state.y - output->ly; - dst_box.width = state.width; - dst_box.height = state.height; + 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) { - dst_box.x += state.border_thickness; - dst_box.width -= state.border_thickness * 2; + 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(); - dst_box.y += titlebar_thickness; - dst_box.height -= state.border_thickness + titlebar_thickness; + clip_box.y += titlebar_thickness; + clip_box.height -= state.border_thickness + titlebar_thickness; } else { // Regular border - dst_box.y += state.border_thickness; - dst_box.height -= state.border_thickness * 2; + clip_box.y += state.border_thickness; + clip_box.height -= state.border_thickness * 2; } } + scale_box(&dst_box, wlr_output->scale); - - deco_data.corner_radius *= wlr_output->scale; - - struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture); + scale_box(&clip_box, wlr_output->scale); // render blur if (deco_data.blur && config_should_parameters_blur()) { - struct wlr_gles2_texture_attribs attribs; - wlr_gles2_texture_get_attribs(saved_buf->buffer->texture, &attribs); - + 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); - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height); - struct blur_stencil_data stencil_data = { &fx_texture, &saved_buf->source_box, matrix }; bool should_optimize_blur = !container_is_floating_or_child(view->container) || config->blur_xray; - render_blur(should_optimize_blur, output, damage, &dst_box, &opaque_region, - &deco_data, &stencil_data); + 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); } @@ -870,54 +687,52 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output deco_data.discard_transparent = false; - // Render saved surface texture - render_texture(wlr_output, damage, &fx_texture, - &saved_buf->source_box, &dst_box, matrix, deco_data); - } + 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 + // 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 sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors, - struct decoration_data deco_data) { +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; - struct sway_container_state *state = &con->current; - // render view if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(view, output, damage, deco_data); + render_saved_view(ctx, view, deco_data); } else if (view->surface) { - render_view_toplevels(view, output, damage, deco_data); + 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; } - float output_scale = output->wlr_output->scale; 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->shadow_blur_sigma > 0 && config->shadow_color[3] > 0.0) { - box.x = floor(state->x) - output->lx; - box.y = floor(state->y) - output->ly; + 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 scaled_corner_radius = deco_data.corner_radius == 0 ? - 0 : (deco_data.corner_radius + state->border_thickness) * 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; - int offset_x = config->shadow_offset_x * output->wlr_output->scale; - int offset_y = config->shadow_offset_y * output->wlr_output->scale; - render_box_shadow(output, damage, &box, shadow_color, config->shadow_blur_sigma, - scaled_corner_radius, offset_x, offset_y); + 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) { @@ -928,22 +743,17 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, deco_data.alpha); + 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; - // adjust sizing for rounded border corners - if (deco_data.corner_radius) { - if (!deco_data.has_titlebar) { - box.y += deco_data.corner_radius; - box.height -= 2 * deco_data.corner_radius; - } else { - box.height -= deco_data.corner_radius; - } + 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(output, damage, &box, color); + render_rect(ctx, &box, color); } list_t *siblings = container_get_current_siblings(con); @@ -956,22 +766,17 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - premultiply_alpha(color, deco_data.alpha); + 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; - // adjust sizing for rounded border corners - if (deco_data.corner_radius) { - if (!deco_data.has_titlebar) { - box.y += deco_data.corner_radius; - box.height -= 2 * deco_data.corner_radius; - } else { - box.height -= deco_data.corner_radius; - } + 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(output, damage, &box, color); + render_rect(ctx, &box, color); } if (state->border_bottom) { @@ -980,42 +785,40 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - premultiply_alpha(color, deco_data.alpha); + 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 += deco_data.corner_radius + state->border_thickness; - box.width -= 2 * (deco_data.corner_radius + state->border_thickness); + box.x += corner_radius + state->border_thickness; + box.width -= 2 * (corner_radius + state->border_thickness); } scale_box(&box, output_scale); - render_rect(output, damage, &box, color); + render_rect(ctx, &box, color); - // rounded bottom left & bottom right border corners - if (deco_data.corner_radius) { - int size = 2 * (deco_data.corner_radius + state->border_thickness); - int scaled_thickness = state->border_thickness * output_scale; - int scaled_corner_radius = deco_data.corner_radius * output_scale; + 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.width = size; - box.height = size; box.x = floor(state->x); box.y = floor(state->y + state->height - size); - scale_box(&box, output_scale); - render_border_corner(output, damage, &box, color, - scaled_corner_radius, scaled_thickness, BOTTOM_LEFT); - } - if (state->border_right) { 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_border_corner(output, damage, &box, color, - scaled_corner_radius, scaled_thickness, BOTTOM_RIGHT); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, BOTTOM_RIGHT); } } } @@ -1030,13 +833,13 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, * 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 sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, int x, int y, - int width, struct border_colors *colors, float alpha, int corner_radius, +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; @@ -1046,17 +849,15 @@ static void render_titlebar(struct sway_output *output, 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; - - if (corner_location == NONE) { - corner_radius = 0; - } + corner_radius *= output_scale; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, alpha); + 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; @@ -1066,47 +867,43 @@ static void render_titlebar(struct sway_output *output, } else { box.width -= corner_radius; } - } else { - box.x += titlebar_border_thickness; - box.width -= titlebar_border_thickness * 2; } - box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Single pixel bar below title - if (config->titlebar_separator) { + 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(output, output_damage, &box, color); + render_rect(ctx, &box, color); } - // Single pixel bar left edge + // Single pixel left edge box.x = x; box.y = y; box.width = titlebar_border_thickness; - box.height = container_titlebar_height() + bottom_border_compensation; + 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(output, output_damage, &box, color); + render_rect(ctx, &box, color); - // Single pixel bar right edge + // Single pixel right edge box.x = x + width - titlebar_border_thickness; box.y = y; box.width = titlebar_border_thickness; - box.height = container_titlebar_height() + bottom_border_compensation; + 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(output, output_damage, &box, color); + render_rect(ctx, &box, color); // if corner_radius: single pixel corners if (corner_radius) { @@ -1117,8 +914,8 @@ static void render_titlebar(struct sway_output *output, box.width = corner_radius * 2; box.height = corner_radius * 2; scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - corner_radius, titlebar_border_thickness, TOP_LEFT); + render_rounded_border_corner(ctx, &box, color, corner_radius, + titlebar_border_thickness * output_scale, TOP_LEFT); } // right corner @@ -1128,8 +925,8 @@ static void render_titlebar(struct sway_output *output, box.width = corner_radius * 2; box.height = corner_radius * 2; scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - corner_radius, titlebar_border_thickness, TOP_RIGHT); + render_rounded_border_corner(ctx, &box, color, corner_radius, + titlebar_border_thickness * output_scale, TOP_RIGHT); } } @@ -1138,7 +935,7 @@ static void render_titlebar(struct sway_output *output, size_t inner_width = width - titlebar_h_padding * 2; // output-buffer local - int ob_inner_x = round(inner_x * output_scale); + 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 + @@ -1146,7 +943,7 @@ static void render_titlebar(struct sway_output *output, // title marks textures should have no eyecandy struct decoration_data deco_data = get_undecorated_decoration_data(); - deco_data.alpha = alpha; + deco_data.alpha = con->alpha; // Marks int ob_marks_x = 0; // output-buffer-local @@ -1176,31 +973,26 @@ static void render_titlebar(struct sway_output *output, texture_box.y = round((bg_y - output_y) * output_scale) + ob_padding_above; - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width < texture_box.width) { - texture_box.width = ob_inner_width; + struct wlr_box clip_box = texture_box; + if (ob_inner_width < clip_box.width) { + clip_box.width = ob_inner_width; } - struct fx_texture fx_texture = fx_texture_from_wlr_texture(marks_texture); - render_texture(output->wlr_output, output_damage, &fx_texture, - NULL, &texture_box, matrix, deco_data); + 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, alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; + 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(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Padding below - box.y += ob_padding_above + texture_box.height; + box.y += ob_padding_above + clip_box.height; box.height = ob_padding_below + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Title text @@ -1223,7 +1015,7 @@ static void render_titlebar(struct sway_output *output, // 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 = round((titlebar_v_padding - + 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; @@ -1252,32 +1044,27 @@ static void render_titlebar(struct sway_output *output, texture_box.y = round((bg_y - output_y) * output_scale) + ob_padding_above; - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width - ob_marks_width < texture_box.width) { - texture_box.width = ob_inner_width - ob_marks_width; + 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; } - struct fx_texture fx_texture = fx_texture_from_wlr_texture(title_texture); - render_texture(output->wlr_output, output_damage, &fx_texture, - NULL, &texture_box, matrix, deco_data); + 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, alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; + 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(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Padding below - box.y += ob_padding_above + texture_box.height; + box.y += ob_padding_above + clip_box.height; box.height = ob_padding_below + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Determine the left + right extends of the textures (output-buffer local) @@ -1309,9 +1096,9 @@ static void render_titlebar(struct sway_output *output, 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 = round(bg_y * output_scale); + box.y = roundf(bg_y * output_scale); box.height = ob_bg_height + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Padding on left side @@ -1325,10 +1112,10 @@ static void render_titlebar(struct sway_output *output, if (box.x + box.width < left_x) { box.width += left_x - box.x - box.width; } - if (corner_radius && corner_location != TOP_RIGHT) { - render_rounded_rect(output, output_damage, &box, color, corner_radius, TOP_LEFT); + if (corner_radius && (corner_location == TOP_LEFT || corner_location == ALL)) { + render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT); } else { - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Padding on right side @@ -1343,67 +1130,57 @@ static void render_titlebar(struct sway_output *output, box.width += box.x - right_rx; box.x = right_rx; } - if (corner_radius && corner_location != TOP_LEFT) { - render_rounded_rect(output, output_damage, &box, color, corner_radius, TOP_RIGHT); + if (corner_radius && (corner_location == TOP_RIGHT || corner_location == ALL)) { + render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT); } else { - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } } /** * Render the top border line for a view using "border pixel". */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container_state *state, - struct border_colors *colors, float alpha, int corner_radius) { +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 = output->wlr_output->scale; + float output_scale = ctx->output->wlr_output->scale; // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, alpha); - box.x = floor(state->x); + premultiply_alpha(color, con->alpha); + box.x = floor(state->x) + corner_radius; box.y = floor(state->y); - box.width = state->width; + box.width = state->width - (2 * corner_radius); box.height = state->border_thickness; - - // adjust sizing for rounded border corners - if (corner_radius) { - box.x += corner_radius + state->border_thickness; - box.width -= 2 * (corner_radius + state->border_thickness); - } scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); - // render rounded top corner borders if corner_radius is set > 0 - if (corner_radius) { + if (corner_radius && state->border_thickness > 0) { int size = 2 * (corner_radius + state->border_thickness); - int scaled_thickness = state->border_thickness * output_scale; int scaled_corner_radius = corner_radius * output_scale; - - // top left + int scaled_border_thickness = state->border_thickness * output_scale; if (state->border_left) { - box.width = size; - box.height = size; box.x = floor(state->x); box.y = floor(state->y); - scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - scaled_corner_radius, scaled_thickness, TOP_LEFT); - } - // top right - if (state->border_right) { 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_border_corner(output, output_damage, &box, color, - scaled_corner_radius, scaled_thickness, TOP_RIGHT); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, TOP_RIGHT); } } } @@ -1416,17 +1193,17 @@ struct parent_data { struct sway_container *active_child; }; -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool parent_focused); +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 sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +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]; @@ -1469,7 +1246,7 @@ static void render_containers_linear(struct sway_output *output, .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 && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : child->corner_radius, .saturation = child->saturation, .has_titlebar = has_titlebar, @@ -1477,16 +1254,16 @@ static void render_containers_linear(struct sway_output *output, .discard_transparent = false, .shadow = child->shadow_enabled, }; - render_view(output, damage, child, colors, deco_data); + render_view(ctx, child, colors, deco_data); if (has_titlebar) { - render_titlebar(output, damage, child, floor(state->x), floor(state->y), - state->width, colors, deco_data.alpha, deco_data.corner_radius, + 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(output, damage, state, colors, deco_data.alpha, deco_data.corner_radius); + render_top_border(ctx, child, colors, deco_data.corner_radius); } } else { - render_container(output, damage, child, + render_container(ctx, child, parent->focused || child->current.focused); } } @@ -1503,8 +1280,7 @@ static bool container_has_focused_child(struct sway_container *con) { /** * Render a container's children using the L_TABBED layout. */ -static void render_containers_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers_tabbed(struct fx_render_context *ctx, struct parent_data *parent) { if (!parent->children->length) { return; } @@ -1520,7 +1296,7 @@ static void render_containers_tabbed(struct sway_output *output, .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 && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : current->corner_radius, .saturation = current->saturation, .has_titlebar = true, @@ -1570,19 +1346,20 @@ static void render_containers_tabbed(struct sway_output *output, } // only round outer corners - enum corner_location corner_location = NONE; - if (i == 0) { - if (i == parent->children->length - 1) { - corner_location = ALL; - } else { + 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; } - } else if (i == parent->children->length - 1) { - corner_location = TOP_RIGHT; } - render_titlebar(output, damage, child, x, parent->box.y, tab_width, colors, - deco_data.alpha, deco_data.corner_radius, corner_location, title_texture, marks_texture); + 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; @@ -1591,9 +1368,9 @@ static void render_containers_tabbed(struct sway_output *output, // Render surface and left/right/bottom borders if (current->view) { - render_view(output, damage, current, current_colors, deco_data); + render_view(ctx, current, current_colors, deco_data); } else { - render_container(output, damage, current, + render_container(ctx, current, parent->focused || current->current.focused); } } @@ -1601,8 +1378,7 @@ static void render_containers_tabbed(struct sway_output *output, /** * Render a container's children using the L_STACKED layout. */ -static void render_containers_stacked(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers_stacked(struct fx_render_context *ctx, struct parent_data *parent) { if (!parent->children->length) { return; } @@ -1618,7 +1394,7 @@ static void render_containers_stacked(struct sway_output *output, .dim = current->current.focused || parent->focused ? 0.0f : current->dim, .saturation = current->saturation, .corner_radius = config->smart_corner_radius && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : current->corner_radius, .has_titlebar = true, .blur = current->blur_enabled, @@ -1661,8 +1437,8 @@ static void render_containers_stacked(struct sway_output *output, int y = parent->box.y + titlebar_height * i; int corner_radius = i != 0 ? 0 : deco_data.corner_radius; - render_titlebar(output, damage, child, parent->box.x, y, parent->box.width, - colors, deco_data.alpha, corner_radius, ALL, title_texture, marks_texture); + 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; @@ -1671,19 +1447,18 @@ static void render_containers_stacked(struct sway_output *output, // Render surface and left/right/bottom borders if (current->view) { - render_view(output, damage, current, current_colors, deco_data); + render_view(ctx, current, current_colors, deco_data); } else { - render_container(output, damage, current, + render_container(ctx, current, parent->focused || current->current.focused); } } -static void render_containers(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +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(output,damage, parent); + render_containers_linear(ctx, parent); return; } } @@ -1692,19 +1467,19 @@ static void render_containers(struct sway_output *output, case L_NONE: case L_HORIZ: case L_VERT: - render_containers_linear(output, damage, parent); + render_containers_linear(ctx, parent); break; case L_STACKED: - render_containers_stacked(output, damage, parent); + render_containers_stacked(ctx, parent); break; case L_TABBED: - render_containers_tabbed(output, damage, parent); + render_containers_tabbed(ctx, parent); break; } } -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool focused) { +static void render_container(struct fx_render_context *ctx, + struct sway_container *con, bool focused) { struct parent_data data = { .layout = con->current.layout, .box = { @@ -1717,11 +1492,11 @@ static void render_container(struct sway_output *output, .focused = focused, .active_child = con->current.focused_inactive_child, }; - render_containers(output, damage, &data); + render_containers(ctx, &data); } -static void render_workspace(struct sway_output *output, - pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { +static void render_workspace(struct fx_render_context *ctx, + struct sway_workspace *ws, bool focused) { struct parent_data data = { .layout = ws->current.layout, .box = { @@ -1734,11 +1509,11 @@ static void render_workspace(struct sway_output *output, .focused = focused, .active_child = ws->current.focused_inactive_child, }; - render_containers(output, damage, &data); + render_containers(ctx, &data); } -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, struct sway_container *con) { +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; @@ -1774,21 +1549,19 @@ static void render_floating_container(struct sway_output *soutput, .discard_transparent = false, .shadow = con->shadow_enabled, }; - render_view(soutput, damage, con, colors, deco_data); + render_view(ctx, con, colors, deco_data); if (has_titlebar) { - render_titlebar(soutput, damage, con, floor(state->x), floor(state->y), - state->width, colors, deco_data.alpha, deco_data.corner_radius, - ALL, title_texture, marks_texture); + 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(soutput, damage, state, colors, deco_data.alpha, deco_data.corner_radius); + render_top_border(ctx, con, colors, deco_data.corner_radius); } } else { - render_container(soutput, damage, con, state->focused); + render_container(ctx, con, state->focused); } } -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { +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) { @@ -1801,24 +1574,26 @@ static void render_floating(struct sway_output *soutput, if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { continue; } - render_floating_container(soutput, damage, floater); + render_floating_container(ctx, floater); } } } } -static void render_seatops(struct sway_output *output, - pixman_region32_t *damage) { +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, output, damage); + seatop_render(seat, ctx); } } -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +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 fx_renderer *renderer = fx_get_renderer(ctx->renderer); struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -1830,47 +1605,45 @@ void output_render(struct sway_output *output, struct timespec *when, fullscreen_con = workspace->current.fullscreen; } - // TODO: generate the monitor box in fx_renderer (since it already has a wlr_output) - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), - monitor_box.width, monitor_box.height); - - fx_renderer_begin(renderer, monitor_box.width, monitor_box.height); - - int output_width, output_height; - wlr_output_transformed_resolution(wlr_output, &output_width, &output_height); - - if (debug.damage == DAMAGE_RERENDER) { - pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); - } - if (!pixman_region32_not_empty(damage)) { // Output isn't damaged but needs buffer swap goto renderer_end; } if (debug.damage == DAMAGE_HIGHLIGHT) { - fx_renderer_clear((float[]){1, 1, 0, 1}); + 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) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + struct wlr_render_color clear_color = { + .a = 1.0f + }; if (server.session_lock.lock == NULL) { // abandoned lock -> red BG - clear_color[0] = 1.f; - } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_renderer_clear(clear_color); + 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 = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; struct wlr_session_lock_surface_v1 *lock_surface; @@ -1878,7 +1651,7 @@ void output_render(struct sway_output *output, struct timespec *when, if (lock_surface->output != wlr_output) { continue; } - if (!lock_surface->mapped) { + if (!lock_surface->surface->mapped) { continue; } @@ -1894,46 +1667,47 @@ void output_render(struct sway_output *output, struct timespec *when, } if (fullscreen_con) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_renderer_clear(clear_color); - } + 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(fullscreen_con->view, output, damage, deco_data); + render_saved_view(ctx, fullscreen_con->view, deco_data); } else if (fullscreen_con->view->surface) { - render_view_toplevels(fullscreen_con->view, output, damage, deco_data); + render_view_toplevels(ctx, fullscreen_con->view, deco_data); } } else { - render_container(output, damage, fullscreen_con, - fullscreen_con->current.focused); + 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(output, damage, floater); + render_floating_container(ctx, floater); } } #if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); + 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 (workspace_has_blur) { // Skip the blur artifact prevention if damaging the whole viewport - if (renderer->blur_buffer_dirty) { + if (effect_fbos->blur_buffer_dirty) { // Needs to be extended before clearing pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); @@ -1970,51 +1744,62 @@ void output_render(struct sway_output *output, struct timespec *when, pixman_region32_fini(&extended_damage); // Capture the padding pixels before blur for later use - fx_framebuffer_bind(&renderer->blur_saved_pixels_buffer); - // TODO: Investigate blitting instead - render_whole_output(renderer, wlr_output, - &renderer->blur_padding_region, &renderer->wlr_buffer.texture); - fx_framebuffer_bind(&renderer->wlr_buffer); + fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, + ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, + ctx->pass->buffer, true); } } pixman_region32_fini(&blur_region); - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + 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, + }, + }); - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - fx_renderer_clear(clear_color); - } - - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - // check if the background needs to be blurred - if (workspace_has_blur && renderer->blur_buffer_dirty) { - render_output_blur(output, damage); + // Check if the background needs to be blurred. + // Render optimized/x-ray blur + if (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(output, damage, workspace, workspace->current.focused); - render_floating(output, damage); + render_workspace(ctx, workspace, workspace->current.focused); + render_floating(ctx); #if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); + render_unmanaged(ctx, &root->xwayland_unmanaged); #endif - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } - render_seatops(output, damage); + render_seatops(ctx); struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); @@ -2032,51 +1817,25 @@ void output_render(struct sway_output *output, struct timespec *when, .discard_transparent = false, .shadow = false, }; - render_view_popups(focus->view, output, damage, deco_data); + render_view_popups(ctx, focus->view, deco_data); } render_overlay: - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root->drag_icons); + render_drag_icons(ctx, &root->drag_icons); renderer_end: // Not needed if we damaged the whole viewport - if (!renderer->blur_buffer_dirty) { + if (!effect_fbos->blur_buffer_dirty) { // Render the saved pixels over the blur artifacts - // TODO: Investigate blitting instead - render_whole_output(renderer, wlr_output, &renderer->blur_padding_region, - &renderer->blur_saved_pixels_buffer.texture); + fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, + ctx->pass->buffer, + ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, true); } - fx_renderer_end(output->renderer); - fx_renderer_scissor(NULL); - - // Draw the software cursors - wlr_renderer_begin(output->server->wlr_renderer, wlr_output->width, wlr_output->height); - wlr_output_render_software_cursors(wlr_output, damage); - wlr_renderer_end(output->server->wlr_renderer); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, damage, transform, output_width, output_height); - - if (debug.damage != DAMAGE_DEFAULT) { - pixman_region32_union_rect(&frame_damage, &frame_damage, - 0, 0, wlr_output->width, wlr_output->height); - } - - wlr_output_set_damage(wlr_output, &frame_damage); - pixman_region32_fini(&frame_damage); - - if (!wlr_output_commit(wlr_output)) { - return; - } - - wlr_damage_ring_rotate(&output->damage_ring); - output->last_frame = *when; + 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 index 1d7b536d..5932eaa2 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -2,8 +2,10 @@ #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); @@ -44,3 +46,27 @@ void handle_compositor_new_surface(struct wl_listener *listener, void *data) { 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/transaction.c b/sway/desktop/transaction.c index f5a3a053..6947e138 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -344,7 +344,7 @@ static void transaction_progress(void) { server.queued_transaction = NULL; if (!server.pending_transaction) { - sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(); return; } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 2d79727d..6dde98bd 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -67,7 +67,13 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { struct sway_view *view = popup->child.view; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - struct sway_output *output = view->container->pending.workspace->output; + struct sway_workspace *workspace = view->container->pending.workspace; + if (!workspace) { + // is null if in the scratchpad + return; + } + + struct sway_output *output = workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup @@ -98,8 +104,8 @@ static struct sway_xdg_popup *popup_create( wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); + 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); @@ -163,12 +169,19 @@ static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - enum wlr_edges edges = WLR_EDGE_NONE; - if (tiled) { - edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | - WLR_EDGE_BOTTOM; + if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= + XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { + enum wlr_edges edges = WLR_EDGE_NONE; + if (tiled) { + edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | + WLR_EDGE_BOTTOM; + } + wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); + } else { + // The version is too low for the tiled state; configure as maximized instead + // to stop the client from drawing decorations outside of the toplevel geometry. + wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); } - wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { @@ -288,6 +301,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); + // Only set the toplevel size the current container actually has a size. + if (view->container->current.width) { + wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, + view->geometry.height); + } transaction_commit_dirty_client(); } else { view_center_surface(view); @@ -356,7 +374,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; struct sway_view *view = &xdg_shell_view->view; - if (!toplevel->base->mapped) { + if (!toplevel->base->surface->mapped) { return; } @@ -550,10 +568,10 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; xdg_shell_view->map.notify = handle_map; - wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); + wl_signal_add(&xdg_surface->surface->events.map, &xdg_shell_view->map); xdg_shell_view->unmap.notify = handle_unmap; - wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); + wl_signal_add(&xdg_surface->surface->events.unmap, &xdg_shell_view->unmap); xdg_shell_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 55a14c0b..709795e8 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -125,8 +125,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { } static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) { - struct wlr_xwayland_surface *xsurface = data; - if (!xsurface->mapped) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, request_activate); + struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } struct sway_seat *seat = input_manager_current_seat(); @@ -138,12 +140,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void seat_set_focus_surface(seat, xsurface->surface, false); } +static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, associate); + struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; + wl_signal_add(&xsurface->surface->events.map, &surface->map); + surface->map.notify = unmanaged_handle_map; + wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); + surface->unmap.notify = unmanaged_handle_unmap; +} + +static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, dissociate); + wl_list_remove(&surface->map.link); + wl_list_remove(&surface->unmap.link); +} + static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, destroy); wl_list_remove(&surface->request_configure.link); - wl_list_remove(&surface->map.link); - wl_list_remove(&surface->unmap.link); + wl_list_remove(&surface->associate.link); + wl_list_remove(&surface->dissociate.link); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->override_redirect.link); wl_list_remove(&surface->request_activate.link); @@ -151,6 +170,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { } static void handle_map(struct wl_listener *listener, void *data); +static void handle_associate(struct wl_listener *listener, void *data); struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); @@ -159,14 +179,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi wl_container_of(listener, surface, override_redirect); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - bool mapped = xsurface->mapped; + bool associated = xsurface->surface != NULL; + bool mapped = associated && xsurface->surface->mapped; if (mapped) { unmanaged_handle_unmap(&surface->unmap, NULL); } + if (associated) { + unmanaged_handle_dissociate(&surface->dissociate, NULL); + } unmanaged_handle_destroy(&surface->destroy, NULL); xsurface->data = NULL; + struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); + if (associated) { + handle_associate(&xwayland_view->associate, NULL); + } if (mapped) { handle_map(&xwayland_view->map, xsurface); } @@ -186,10 +214,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged( wl_signal_add(&xsurface->events.request_configure, &surface->request_configure); surface->request_configure.notify = unmanaged_handle_request_configure; - wl_signal_add(&xsurface->events.map, &surface->map); - surface->map.notify = unmanaged_handle_map; - wl_signal_add(&xsurface->events.unmap, &surface->unmap); - surface->unmap.notify = unmanaged_handle_unmap; + wl_signal_add(&xsurface->events.associate, &surface->associate); + surface->associate.notify = unmanaged_handle_associate; + wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); + surface->dissociate.notify = unmanaged_handle_dissociate; wl_signal_add(&xsurface->events.destroy, &surface->destroy); surface->destroy.notify = unmanaged_handle_destroy; wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); @@ -472,8 +500,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_decorations.link); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); + wl_list_remove(&xwayland_view->associate.link); + wl_list_remove(&xwayland_view->dissociate.link); wl_list_remove(&xwayland_view->override_redirect.link); view_begin_destroy(&xwayland_view->view); } @@ -495,8 +523,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { static void handle_map(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, map); - struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; view->natural_width = xsurface->width; view->natural_height = xsurface->height; @@ -512,20 +540,30 @@ static void handle_map(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } +static void handle_dissociate(struct wl_listener *listener, void *data); + static void handle_override_redirect(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, override_redirect); - struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - bool mapped = xsurface->mapped; + bool associated = xsurface->surface != NULL; + bool mapped = associated && xsurface->surface->mapped; if (mapped) { handle_unmap(&xwayland_view->unmap, NULL); } + if (associated) { + handle_dissociate(&xwayland_view->dissociate, NULL); + } handle_destroy(&xwayland_view->destroy, view); xsurface->data = NULL; + struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); + if (associated) { + unmanaged_handle_associate(&unmanaged->associate, NULL); + } if (mapped) { unmanaged_handle_map(&unmanaged->map, xsurface); } @@ -537,7 +575,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface_configure_event *ev = data; struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, ev->height); return; @@ -566,7 +604,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) wl_container_of(listener, xwayland_view, request_fullscreen); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } container_set_fullscreen(view->container, xsurface->fullscreen); @@ -581,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_minimize); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } @@ -617,7 +655,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_move); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } if (!container_is_floating(view->container) || @@ -633,7 +671,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_resize); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } if (!container_is_floating(view->container)) { @@ -649,10 +687,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_activate); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } - view_request_activate(view); + view_request_activate(view, NULL); transaction_commit_dirty(); } @@ -662,7 +700,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_title); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_update_title(view, false); @@ -674,7 +712,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_class); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -685,7 +723,7 @@ static void handle_set_role(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_role); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -721,7 +759,7 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_window_type); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -732,7 +770,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_hints); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); @@ -747,6 +785,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { } } +static void handle_associate(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, associate); + struct wlr_xwayland_surface *xsurface = + xwayland_view->view.wlr_xwayland_surface; + wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); + xwayland_view->unmap.notify = handle_unmap; + wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); + xwayland_view->map.notify = handle_map; +} + +static void handle_dissociate(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, dissociate); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); +} + struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface) { return xsurface->data; @@ -816,11 +872,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu &xwayland_view->set_decorations); xwayland_view->set_decorations.notify = handle_set_decorations; - wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); - xwayland_view->unmap.notify = handle_unmap; + wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); + xwayland_view->associate.notify = handle_associate; - wl_signal_add(&xsurface->events.map, &xwayland_view->map); - xwayland_view->map.notify = handle_map; + wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); + xwayland_view->dissociate.notify = handle_dissociate; wl_signal_add(&xsurface->events.set_override_redirect, &xwayland_view->override_redirect); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index a5f1204b..36aab93e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,12 +53,10 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output, } static bool surface_is_xdg_popup(struct wlr_surface *surface) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; - } - return false; + 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, @@ -239,7 +237,7 @@ void cursor_update_image(struct sway_cursor *cursor, // Try a node's resize edge enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "left_ptr", NULL); + cursor_set_image(cursor, "default", NULL); } else if (container_is_floating(node->sway_container)) { cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); } else { @@ -250,12 +248,12 @@ void cursor_update_image(struct sway_cursor *cursor, } } } else { - cursor_set_image(cursor, "left_ptr", NULL); + cursor_set_image(cursor, "default", NULL); } } static void cursor_hide(struct sway_cursor *cursor) { - wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + wlr_cursor_unset_image(cursor->cursor); cursor->hidden = true; wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); } @@ -367,7 +365,7 @@ void cursor_unhide(struct sway_cursor *cursor) { wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); } -static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, +void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { wlr_relative_pointer_manager_v1_send_relative_motion( @@ -482,43 +480,16 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { cursor_hide(cursor); struct sway_seat *seat = cursor->seat; - struct wlr_seat *wlr_seat = seat->wlr_seat; - struct wlr_surface *surface = NULL; double lx, ly; wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, event->x, event->y, &lx, &ly); - double sx, sy; - struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); seat->touch_id = event->touch_id; seat->touch_x = lx; seat->touch_y = ly; - if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { - if (seat_is_input_allowed(seat, surface)) { - wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, - event->touch_id, sx, sy); - - if (focused_node) { - seat_set_focus(seat, focused_node); - } - } - } else if (!cursor->simulating_pointer_from_touch && - (!surface || seat_is_input_allowed(seat, surface))) { - // Fallback to cursor simulation. - // The pointer_touch_id state is needed, so drags are not aborted when over - // a surface supporting touch and multi touch events don't interfere. - cursor->simulating_pointer_from_touch = true; - cursor->pointer_touch_id = seat->touch_id; - double dx, dy; - dx = lx - cursor->cursor->x; - dy = ly - cursor->cursor->y; - 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); - } + seatop_touch_down(seat, event, lx, ly); } static void handle_touch_up(struct wl_listener *listener, void *data) { @@ -526,7 +497,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { struct wlr_touch_up_event *event = data; cursor_handle_activity_from_device(cursor, &event->touch->base); - struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; + struct sway_seat *seat = cursor->seat; if (cursor->simulating_pointer_from_touch) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { @@ -535,7 +506,25 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); } } else { - wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); + seatop_touch_up(seat, event); + } +} + +static void handle_touch_cancel(struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); + struct wlr_touch_cancel_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); + + struct sway_seat *seat = cursor->seat; + + if (cursor->simulating_pointer_from_touch) { + 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); + } + } else { + seatop_touch_cancel(seat, event); } } @@ -546,19 +535,14 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { cursor_handle_activity_from_device(cursor, &event->touch->base); struct sway_seat *seat = cursor->seat; - struct wlr_seat *wlr_seat = seat->wlr_seat; - struct wlr_surface *surface = NULL; double lx, ly; wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base, event->x, event->y, &lx, &ly); - double sx, sy; - node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); 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) { @@ -575,9 +559,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, dx, dy); } - } else if (surface) { - wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, - event->touch_id, sx, sy); + } else { + seatop_touch_motion(seat, event, lx, ly); } } @@ -836,7 +819,34 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + // TODO: floating resize should support graphics tablet events + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + bool mod_pressed = modifiers & config->floating_mod; + + bool surface_supports_tablet_events = + surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); + + // Simulate pointer when: + // 1. The modifier key is pressed, OR + // 2. The surface under the cursor does not support tablet events. + bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events; + + // Similar to tool tip, we need to selectively simulate mouse events, but we + // want to make sure that it is always consistent. Because all tool buttons + // currently map to BTN_RIGHT, we need to keep count of how many tool + // buttons are currently pressed down so we can send consistent events. + // + // The logic follows: + // - If we are already simulating the pointer, we should continue to do so + // until at least no tool button is held down. + // - If we should simulate the pointer and no tool button is currently held + // down, begin simulating the pointer. + // - If neither of the above are true, send the tablet events. + if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button) + || (cursor->tool_buttons == 0 && should_simulate_pointer)) { + cursor->simulating_pointer_from_tool_button = true; + // TODO: the user may want to configure which tool buttons are mapped to // which simulated pointer buttons switch (event->state) { @@ -845,22 +855,35 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_RIGHT, event->state); } - cursor->tool_buttons++; break; case WLR_BUTTON_RELEASED: - if (cursor->tool_buttons == 1) { + if (cursor->tool_buttons <= 1) { dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_RIGHT, event->state); } - cursor->tool_buttons--; break; } wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - return; + } else { + cursor->simulating_pointer_from_tool_button = false; + + wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, + event->button, (enum zwp_tablet_pad_v2_button_state)event->state); } - wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, - event->button, (enum zwp_tablet_pad_v2_button_state)event->state); + // Update tool button count. + switch (event->state) { + case WLR_BUTTON_PRESSED: + cursor->tool_buttons++; + break; + case WLR_BUTTON_RELEASED: + if (cursor->tool_buttons == 0) { + sway_log(SWAY_ERROR, "inconsistent tablet tool button events"); + } else { + cursor->tool_buttons--; + } + break; + } } static void check_constraint_region(struct sway_cursor *cursor) { @@ -1046,10 +1069,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, } if (!image) { - wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + wlr_cursor_unset_image(cursor->cursor); } else if (!current_image || strcmp(current_image, image) != 0) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, - cursor->cursor); + wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); } } @@ -1096,6 +1118,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_list_remove(&cursor->frame.link); wl_list_remove(&cursor->touch_down.link); wl_list_remove(&cursor->touch_up.link); + wl_list_remove(&cursor->touch_cancel.link); wl_list_remove(&cursor->touch_motion.link); wl_list_remove(&cursor->touch_frame.link); wl_list_remove(&cursor->tool_axis.link); @@ -1132,9 +1155,6 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_list_init(&cursor->image_surface_destroy.link); cursor->image_surface_destroy.notify = handle_image_surface_destroy; - // gesture events - cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); - wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin); cursor->hold_begin.notify = handle_pointer_hold_begin; wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end); @@ -1177,6 +1197,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); cursor->touch_up.notify = handle_touch_up; + wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); + cursor->touch_cancel.notify = handle_touch_cancel; + wl_signal_add(&wlr_cursor->events.touch_motion, &cursor->touch_motion); cursor->touch_motion.notify = handle_touch_motion; @@ -1269,11 +1292,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); if (code == -1) { - size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "Unknown event %s", name); - } + *error = format_str("Unknown event %s", name); return 0; } return code; @@ -1295,13 +1314,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { } const char *event = libevdev_event_code_get_name(EV_KEY, code); if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { - size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", - code, event ? event : "(null)") + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "Event code %d (%s) is not a button", - code, event ? event : "(null)"); - } + *error = format_str("Event code %d (%s) is not a button", + code, event ? event : "(null)"); return 0; } return code; @@ -1454,3 +1468,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, wl_signal_add(&constraint->surface->events.commit, &cursor->constraint_commit); } + +void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { + const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; + struct sway_seat *seat = event->seat_client->seat->data; + + if (!seatop_allows_set_cursor(seat)) { + return; + } + + struct wl_client *focused_client = NULL; + struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; + if (focused_surface != NULL) { + focused_client = wl_resource_get_client(focused_surface->resource); + } + + // TODO: check cursor mode + if (focused_client == NULL || event->seat_client->client != focused_client) { + sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); + return; + } + + cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); +} diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 39f4b795..288fddc4 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -22,6 +22,10 @@ #include "list.h" #include "log.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + #define DEFAULT_SEAT "seat0" struct input_config *current_input_config = NULL; @@ -76,20 +80,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) { } } - const char *fmt = "%d:%d:%s"; - int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; - char *identifier = malloc(len); - if (!identifier) { - sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); - return NULL; - } - - snprintf(identifier, len, fmt, vendor, product, name); + char *identifier = format_str("%d:%d:%s", vendor, product, name); free(name); return identifier; } static bool device_is_touchpad(struct sway_input_device *device) { +#if WLR_HAS_LIBINPUT_BACKEND if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || !wlr_input_device_is_libinput(device->wlr_device)) { return false; @@ -99,6 +96,9 @@ static bool device_is_touchpad(struct sway_input_device *device) { wlr_libinput_get_device_handle(device->wlr_device); return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +#else + return false; +#endif } const char *input_device_get_type(struct sway_input_device *device) { @@ -236,7 +236,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) { apply_input_type_config(input_device); +#if WLR_HAS_LIBINPUT_BACKEND bool config_changed = sway_input_configure_libinput_device(input_device); +#else + bool config_changed = false; +#endif wl_signal_add(&device->events.destroy, &input_device->device_destroy); input_device->device_destroy.notify = handle_device_destroy; @@ -491,6 +495,8 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, &input->keyboard_shortcuts_inhibit_new_inhibitor); + input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); + return input; } @@ -528,11 +534,27 @@ static void retranslate_keysyms(struct input_config *input_config) { return; } } + + for (int i = 0; i < config->input_type_configs->length; ++i) { + struct input_config *ic = config->input_type_configs->items[i]; + if (ic->xkb_layout || ic->xkb_file) { + // this is the first config with xkb_layout or xkb_file + if (ic->identifier == input_config->identifier) { + translate_keysyms(ic); + } + + return; + } + } } static void input_manager_configure_input( struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND bool config_changed = sway_input_configure_libinput_device(input_device); +#else + bool config_changed = false; +#endif struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_configure_device(seat, input_device); @@ -542,10 +564,20 @@ static void input_manager_configure_input( } } -void input_manager_configure_all_inputs(void) { - struct sway_input_device *input_device = NULL; +void input_manager_configure_all_input_mappings(void) { + struct sway_input_device *input_device; wl_list_for_each(input_device, &server.input->devices, link) { - input_manager_configure_input(input_device); + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_configure_device_mapping(seat, input_device); + } + +#if WLR_HAS_LIBINPUT_BACKEND + // Input devices mapped to unavailable outputs get their libinput + // send_events setting switched to false. We need to re-enable this + // when the output appears. + sway_input_configure_libinput_device_send_events(input_device); +#endif } } @@ -567,7 +599,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { } void input_manager_reset_input(struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND sway_input_reset_libinput_device(input_device); +#endif struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_reset_device(seat, input_device); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c5a646c4..8927287f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1,10 +1,9 @@ #include #include #include +#include #include -#include #include -#include #include #include #include @@ -16,6 +15,10 @@ #include "sway/ipc-server.h" #include "log.h" +#if WLR_HAS_SESSION +#include +#endif + static struct modifier_key { char *name; uint32_t mod; @@ -264,14 +267,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, xkb_keysym_t keysym = pressed_keysyms[i]; if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { - if (wlr_backend_is_multi(server.backend)) { - struct wlr_session *session = - wlr_backend_get_session(server.backend); - if (session) { - unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; - wlr_session_change_vt(session, vt); - } +#if WLR_HAS_SESSION + if (server.session) { + unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; + wlr_session_change_vt(server.session, vt); } +#endif return true; } } @@ -715,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, static void handle_xkb_context_log(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args) { - va_list args_copy; - va_copy(args_copy, args); - size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; - va_end(args_copy); + char *error = vformat_str(format, args); - char *error = malloc(length); - if (!error) { - sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); - return; - } - - va_copy(args_copy, args); - vsnprintf(error, length, format, args_copy); - va_end(args_copy); - - if (error[length - 2] == '\n') { - error[length - 2] = '\0'; + size_t len = strlen(error); + if (error[len - 1] == '\n') { + error[len - 1] = '\0'; } sway_log_importance_t importance = SWAY_DEBUG; @@ -752,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, char **error) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); if (!sway_assert(context, "cannot create XKB context")) { return NULL; } @@ -766,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, if (!keymap_file) { sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); if (error) { - size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", - ic->xkb_file, strerror(errno)) + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "cannot read xkb_file %s: %s", - ic->xkb_file, strerror(errno)); - } + *error = format_str("cannot read xkb file %s: %s", + ic->xkb_file, strerror(errno)); } goto cleanup; } diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 53019301..0266c7a9 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) { return true; } +static bool set_rotation_angle(struct libinput_device *device, double angle) { + if (!libinput_device_config_rotation_is_available(device) || + libinput_device_config_rotation_get_angle(device) == angle) { + return false; + } + sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle); + log_status(libinput_device_config_rotation_set_angle(device, angle)); + return true; +} + static bool set_accel_profile(struct libinput_device *device, enum libinput_config_accel_profile profile) { if (!libinput_device_config_accel_is_available(device) || @@ -156,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { return true; } +static bool set_scroll_button_lock(struct libinput_device *dev, + enum libinput_config_scroll_button_lock_state lock) { + uint32_t scroll = libinput_device_config_scroll_get_methods(dev); + if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_button_lock(dev) == lock) { + return false; + } + sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock); + log_status(libinput_device_config_scroll_set_button_lock(dev, lock)); + return true; +} + static bool set_dwt(struct libinput_device *device, bool dwt) { if (!libinput_device_config_dwt_is_available(device) || libinput_device_config_dwt_get_enabled(device) == dwt) { @@ -197,6 +219,26 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { return changed; } +static bool configure_send_events(struct libinput_device *device, + struct input_config *ic) { + if (ic->mapped_to_output && + strcmp("*", ic->mapped_to_output) != 0 && + !output_by_name_or_id(ic->mapped_to_output)) { + sway_log(SWAY_DEBUG, + "%s '%s' is mapped to offline output '%s'; disabling input", + ic->input_type, ic->identifier, ic->mapped_to_output); + return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + } else if (ic->send_events != INT_MIN) { + return set_send_events(device, ic->send_events); + } else { + // Have to reset to the default mode here, otherwise if ic->send_events + // is unset and a mapped output just came online after being disabled, + // we'd remain stuck sending no events. + return set_send_events(device, + libinput_device_config_send_events_get_default_mode(device)); + } +} + bool sway_input_configure_libinput_device(struct sway_input_device *input_device) { struct input_config *ic = input_device_get_config(input_device); if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { @@ -208,24 +250,7 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", ic->identifier, input_device->identifier); - bool changed = false; - if (ic->mapped_to_output && - !output_by_name_or_id(ic->mapped_to_output)) { - sway_log(SWAY_DEBUG, - "%s '%s' is mapped to offline output '%s'; disabling input", - ic->input_type, ic->identifier, ic->mapped_to_output); - changed |= set_send_events(device, - LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); - } else if (ic->send_events != INT_MIN) { - changed |= set_send_events(device, ic->send_events); - } else { - // Have to reset to the default mode here, otherwise if ic->send_events - // is unset and a mapped output just came online after being disabled, - // we'd remain stuck sending no events. - changed |= set_send_events(device, - libinput_device_config_send_events_get_default_mode(device)); - } - + bool changed = configure_send_events(device, ic); if (ic->tap != INT_MIN) { changed |= set_tap(device, ic->tap); } @@ -241,6 +266,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->pointer_accel != FLT_MIN) { changed |= set_accel_speed(device, ic->pointer_accel); } + if (ic->rotation_angle != FLT_MIN) { + changed |= set_rotation_angle(device, ic->rotation_angle); + } if (ic->accel_profile != INT_MIN) { changed |= set_accel_profile(device, ic->accel_profile); } @@ -262,6 +290,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->scroll_button != INT_MIN) { changed |= set_scroll_button(device, ic->scroll_button); } + if (ic->scroll_button_lock != INT_MIN) { + changed |= set_scroll_button_lock(device, ic->scroll_button_lock); + } if (ic->dwt != INT_MIN) { changed |= set_dwt(device, ic->dwt); } @@ -275,6 +306,22 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device return changed; } +void sway_input_configure_libinput_device_send_events( + struct sway_input_device *input_device) { + struct input_config *ic = input_device_get_config(input_device); + if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { + return; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + bool changed = configure_send_events(device, ic); + + if (changed) { + ipc_event_input("libinput_config", input_device); + } +} + void sway_input_reset_libinput_device(struct sway_input_device *input_device) { if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; @@ -298,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_tap_get_default_drag_lock_enabled(device)); changed |= set_accel_speed(device, libinput_device_config_accel_get_default_speed(device)); + changed |= set_rotation_angle(device, + libinput_device_config_rotation_get_default_angle(device)); changed |= set_accel_profile(device, libinput_device_config_accel_get_default_profile(device)); changed |= set_natural_scroll(device, diff --git a/sway/input/seat.c b/sway/input/seat.c index 28210bb5..43289598 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -4,9 +4,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -99,22 +99,10 @@ void seat_destroy(struct sway_seat *seat) { void seat_idle_notify_activity(struct sway_seat *seat, enum sway_input_idle_source source) { - uint32_t mask = seat->idle_inhibit_sources; - struct wlr_idle_timeout *timeout; - int ntimers = 0, nidle = 0; - wl_list_for_each(timeout, &server.idle->idle_timers, link) { - ++ntimers; - if (timeout->idle_state) { - ++nidle; - } - } - if (nidle == ntimers) { - mask = seat->idle_wake_sources; - } - if ((source & mask) > 0) { - wlr_idle_notify_activity(server.idle, seat->wlr_seat); - wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); + if ((source & seat->idle_inhibit_sources) == 0) { + return; } + wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); } /** @@ -366,7 +354,7 @@ static void handle_new_node(struct wl_listener *listener, void *data) { } static void drag_icon_damage_whole(struct sway_drag_icon *icon) { - if (!icon->wlr_drag_icon->mapped) { + if (!icon->wlr_drag_icon->surface->mapped) { return; } desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); @@ -382,8 +370,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { case WLR_DRAG_GRAB_KEYBOARD: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x + wlr_icon->surface->sx; - icon->y = cursor->y + wlr_icon->surface->sy; + icon->x = cursor->x + icon->dx; + icon->y = cursor->y + icon->dy; break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -391,8 +379,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + wlr_icon->surface->sx; - icon->y = seat->touch_y + wlr_icon->surface->sy; + icon->x = seat->touch_x + icon->dx; + icon->y = seat->touch_y + icon->dy; } drag_icon_damage_whole(icon); @@ -402,6 +390,9 @@ 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); } @@ -507,9 +498,9 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { 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->events.unmap, &icon->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->events.map, &icon->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); @@ -667,7 +658,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { } else { wlr_seat_set_capabilities(seat->wlr_seat, caps); if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { - cursor_set_image(seat->cursor, "left_ptr", NULL); + cursor_set_image(seat->cursor, "default", NULL); } } } @@ -714,12 +705,21 @@ static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { } } -static void seat_apply_input_config(struct sway_seat *seat, +static void seat_apply_input_mapping(struct sway_seat *seat, struct sway_seat_device *sway_device) { struct input_config *ic = input_device_get_config(sway_device->input_device); - sway_log(SWAY_DEBUG, "Applying input config to %s", + switch (sway_device->input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + case WLR_INPUT_DEVICE_TOUCH: + case WLR_INPUT_DEVICE_TABLET_TOOL: + break; + default: + return; // these devices don't support mappings + } + + sway_log(SWAY_DEBUG, "Applying input mapping to %s", sway_device->input_device->identifier); const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; @@ -747,6 +747,7 @@ static void seat_apply_input_config(struct sway_seat *seat, mapped_to_output = NULL; break; } +#if WLR_HAS_LIBINPUT_BACKEND if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && sway_libinput_device_is_builtin(sway_device->input_device)) { mapped_to_output = get_builtin_output_name(); @@ -755,6 +756,10 @@ static void seat_apply_input_config(struct sway_seat *seat, mapped_to_output, sway_device->input_device->identifier); } } +#else + (void)is_touch_or_tablet_tool; + (void)get_builtin_output_name; +#endif if (mapped_to_output == NULL) { return; } @@ -798,12 +803,9 @@ static void seat_apply_input_config(struct sway_seat *seat, static void seat_configure_pointer(struct sway_seat *seat, struct sway_seat_device *sway_device) { - if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { - seat_configure_xcursor(seat); - } + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); wl_event_source_timer_update( seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); } @@ -838,7 +840,6 @@ static void seat_configure_switch(struct sway_seat *seat, if (!seat_device->switch_device) { sway_switch_create(seat, seat_device); } - seat_apply_input_config(seat, seat_device); sway_switch_configure(seat_device->switch_device); } @@ -846,7 +847,6 @@ static void seat_configure_touch(struct sway_seat *seat, struct sway_seat_device *sway_device) { wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); } static void seat_configure_tablet_tool(struct sway_seat *seat, @@ -857,7 +857,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, sway_configure_tablet(sway_device->tablet); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); } static void seat_configure_tablet_pad(struct sway_seat *seat, @@ -914,6 +913,18 @@ void seat_configure_device(struct sway_seat *seat, seat_configure_tablet_pad(seat, seat_device); break; } + + seat_apply_input_mapping(seat, seat_device); +} + +void seat_configure_device_mapping(struct sway_seat *seat, + struct sway_input_device *input_device) { + struct sway_seat_device *seat_device = seat_get_device(seat, input_device); + if (!seat_device) { + return; + } + + seat_apply_input_mapping(seat, seat_device); } void seat_reset_device(struct sway_seat *seat, @@ -1030,7 +1041,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( - server.xwayland.xcursor_manager, "left_ptr", 1); + server.xwayland.xcursor_manager, "default", 1); if (xcursor != NULL) { struct wlr_xcursor_image *image = xcursor->images[0]; wlr_xwayland_set_cursor( @@ -1056,26 +1067,27 @@ void seat_configure_xcursor(struct sway_seat *seat) { sway_log(SWAY_ERROR, "Cannot create XCursor manager for theme '%s'", cursor_theme); } - } - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *sway_output = root->outputs->items[i]; - struct wlr_output *output = sway_output->wlr_output; - bool result = - wlr_xcursor_manager_load(seat->cursor->xcursor_manager, - output->scale); - if (!result) { - sway_log(SWAY_ERROR, - "Cannot load xcursor theme for output '%s' with scale %f", - output->name, output->scale); + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *sway_output = root->outputs->items[i]; + struct wlr_output *output = sway_output->wlr_output; + bool result = + wlr_xcursor_manager_load(seat->cursor->xcursor_manager, + output->scale); + if (!result) { + sway_log(SWAY_ERROR, + "Cannot load xcursor theme for output '%s' with scale %f", + output->name, output->scale); + } } - } - // Reset the cursor so that we apply it to outputs that just appeared - cursor_set_image(seat->cursor, NULL, NULL); - cursor_set_image(seat->cursor, "left_ptr", NULL); - wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, - seat->cursor->cursor->y); + // Reset the cursor so that we apply it to outputs that just appeared + cursor_set_image(seat->cursor, NULL, NULL); + cursor_set_image(seat->cursor, "default", NULL); + wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, + seat->cursor->cursor->y); + } } bool seat_is_input_allowed(struct sway_seat *seat, @@ -1286,11 +1298,15 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { - if (seat->focused_layer) { + // Prevents the layer from losing focus if it has keyboard exclusivity + if (seat->has_exclusive_layer) { struct wlr_layer_surface_v1 *layer = seat->focused_layer; seat_set_focus_layer(seat, NULL); seat_set_workspace_focus(seat, node); seat_set_focus_layer(seat, layer); + } else if (seat->focused_layer) { + seat_set_focus_layer(seat, NULL); + seat_set_workspace_focus(seat, node); } else { seat_set_workspace_focus(seat, node); } @@ -1338,14 +1354,20 @@ void seat_set_focus_layer(struct sway_seat *seat, seat_set_focus(seat, previous); } return; - } else if (!layer || seat->focused_layer == layer) { + } else if (!layer) { return; } - assert(layer->mapped); - seat_set_focus_surface(seat, layer->surface, true); - if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { - seat->focused_layer = layer; + assert(layer->surface->mapped); + if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && + layer->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + seat->has_exclusive_layer = true; } + if (seat->focused_layer == layer) { + return; + } + seat_set_focus_surface(seat, layer->surface, true); + seat->focused_layer = layer; } void seat_set_exclusive_client(struct sway_seat *seat, @@ -1609,6 +1631,32 @@ void seatop_pointer_axis(struct sway_seat *seat, } } +void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event, + double lx, double ly) { + if (seat->seatop_impl->touch_motion) { + seat->seatop_impl->touch_motion(seat, event, lx, ly); + } +} + +void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) { + if (seat->seatop_impl->touch_up) { + seat->seatop_impl->touch_up(seat, event); + } +} + +void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event, + double lx, double ly) { + if (seat->seatop_impl->touch_down) { + seat->seatop_impl->touch_down(seat, event, lx, ly); + } +} + +void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) { + if (seat->seatop_impl->touch_cancel) { + seat->seatop_impl->touch_cancel(seat, event); + } +} + void seatop_tablet_tool_tip(struct sway_seat *seat, struct sway_tablet_tool *tool, uint32_t time_msec, enum wlr_tablet_tool_tip_state state) { @@ -1697,10 +1745,9 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage) { +void seatop_render(struct sway_seat *seat, struct fx_render_context *ctx) { if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, output, damage); + seat->seatop_impl->render(seat, ctx); } } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 84acefdf..1dce6dae 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "gesture.h" @@ -9,6 +10,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/input/tablet.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -228,14 +230,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, struct sway_container *cont = node && node->type == N_CONTAINER ? node->sway_container : NULL; - if (wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface_v1 *layer; +#if HAVE_XWAYLAND + struct wlr_xwayland_surface *xsurface; +#endif + if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && + layer->current.keyboard_interactive) { // Handle tapping a layer surface - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - transaction_commit_dirty(); - } + seat_set_focus_layer(seat, layer); + transaction_commit_dirty(); } else if (cont) { bool is_floating_or_child = container_is_floating_or_child(cont); bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); @@ -260,20 +263,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, // Handle tapping on a container surface seat_set_focus_container(seat, cont); - seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); + seatop_begin_down(seat, node->sway_container, sx, sy); } #if HAVE_XWAYLAND // Handle tapping on an xwayland unmanaged view - else if (wlr_surface_is_xwayland_surface(surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(surface); - if (xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - } + else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && + xsurface->override_redirect && + wlr_xwayland_or_surface_wants_focus(xsurface)) { + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); + transaction_commit_dirty(); } #endif @@ -367,16 +367,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, return; } - // Handle clicking a layer surface - if (surface && wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); + // Handle clicking a layer surface and its popups/subsurfaces + struct wlr_layer_surface_v1 *layer = NULL; + if ((layer = toplevel_layer_surface_from_surface(surface))) { if (layer->current.keyboard_interactive) { seat_set_focus_layer(seat, layer); transaction_commit_dirty(); } if (state == WLR_BUTTON_PRESSED) { - seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); + seatop_begin_down_on_surface(seat, surface, sx, sy); } seat_pointer_notify_button(seat, time_msec, button, state); return; @@ -501,7 +500,7 @@ 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) { - seatop_begin_down(seat, cont, time_msec, sx, sy); + seatop_begin_down(seat, cont, sx, sy); seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; } @@ -514,18 +513,16 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, #if HAVE_XWAYLAND // Handle clicking on xwayland unmanaged view - if (surface && wlr_surface_is_xwayland_surface(surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(surface); - if (xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } + struct wlr_xwayland_surface *xsurface; + if (surface && + (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && + xsurface->override_redirect && + wlr_xwayland_or_surface_wants_focus(xsurface)) { + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); + transaction_commit_dirty(); + seat_pointer_notify_button(seat, time_msec, button, state); } #endif @@ -548,6 +545,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat, if (wlr_output == NULL) { return; } + + struct wlr_surface *surface = NULL; + double sx, sy; + node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y, + &surface, &sx, &sy); + + // Focus topmost layer surface + struct wlr_layer_surface_v1 *layer = NULL; + if ((layer = toplevel_layer_surface_from_surface(surface)) && + layer->current.keyboard_interactive) { + seat_set_focus_layer(seat, layer); + transaction_commit_dirty(); + return; + } + struct sway_output *hovered_output = wlr_output->data; if (focus && hovered_output != node_get_output(focus)) { struct sway_workspace *ws = output_get_active_workspace(hovered_output); @@ -653,6 +665,36 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, e->previous_node = node; } +static void handle_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly) { + struct wlr_surface *surface = NULL; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct sway_cursor *cursor = seat->cursor; + double sx, sy; + node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); + + if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { + if (seat_is_input_allowed(seat, surface)) { + cursor->simulating_pointer_from_touch = false; + seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); + } + } else if (!cursor->simulating_pointer_from_touch && + (!surface || seat_is_input_allowed(seat, surface))) { + // Fallback to cursor simulation. + // The pointer_touch_id state is needed, so drags are not aborted when over + // a surface supporting touch and multi touch events don't interfere. + cursor->simulating_pointer_from_touch = true; + cursor->pointer_touch_id = seat->touch_id; + double dx, dy; + dx = seat->touch_x - cursor->cursor->x; + dy = seat->touch_y - cursor->cursor->y; + 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); + } +} + /*----------------------------------------\ * Functions used by handle_pointer_axis / *--------------------------------------*/ @@ -726,7 +768,7 @@ static void handle_pointer_axis(struct sway_seat *seat, seat_get_active_tiling_child(seat, tabcontainer); list_t *siblings = container_get_siblings(cont); int desired = list_find(siblings, active->sway_container) + - round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); + roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); if (desired < 0) { desired = 0; } else if (desired >= siblings->length) { @@ -761,7 +803,7 @@ 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, - round(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source); } } @@ -907,7 +949,7 @@ static void handle_hold_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_hold_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -919,7 +961,7 @@ static void handle_hold_end(struct sway_seat *seat, if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) { struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_hold_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -952,7 +994,7 @@ static void handle_pinch_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_pinch_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -968,7 +1010,7 @@ static void handle_pinch_update(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_pinch_update( - cursor->pointer_gestures, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy, event->scale, event->rotation); @@ -982,7 +1024,7 @@ static void handle_pinch_end(struct sway_seat *seat, if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) { struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_pinch_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -1015,7 +1057,7 @@ static void handle_swipe_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_swipe_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -1032,7 +1074,7 @@ static void handle_swipe_update(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_swipe_update( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy); } } @@ -1043,7 +1085,7 @@ static void handle_swipe_end(struct sway_seat *seat, struct seatop_default_event *seatop = seat->seatop_data; if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) { struct sway_cursor *cursor = seat->cursor; - wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures, + wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -1100,6 +1142,7 @@ static const struct sway_seatop_impl seatop_impl = { .swipe_begin = handle_swipe_begin, .swipe_update = handle_swipe_update, .swipe_end = handle_swipe_end, + .touch_down = handle_touch_down, .rebase = handle_rebase, .allow_set_cursor = true, }; diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index b40773d0..36f9bb60 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -2,12 +2,20 @@ #include #include #include +#include #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" #include "sway/desktop/transaction.h" #include "log.h" +struct seatop_touch_point_event { + double ref_lx, ref_ly; // touch's x/y at start of op + double ref_con_lx, ref_con_ly; // container's x/y at start of op + int32_t touch_id; + struct wl_list link; +}; + struct seatop_down_event { struct sway_container *con; struct sway_seat *seat; @@ -15,8 +23,109 @@ struct seatop_down_event { struct wlr_surface *surface; double ref_lx, ref_ly; // cursor's x/y at start of op double ref_con_lx, ref_con_ly; // container's x/y at start of op + struct wl_list point_events; // seatop_touch_point_event::link }; +static void handle_touch_motion(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly) { + struct seatop_down_event *e = seat->seatop_data; + + struct seatop_touch_point_event *point_event; + bool found = false; + wl_list_for_each(point_event, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + found = true; + break; + } + } + if (!found) { + return; // Probably not a point_event from this seatop_down + } + + double moved_x = lx - point_event->ref_lx; + double moved_y = ly - point_event->ref_ly; + double sx = point_event->ref_con_lx + moved_x; + double sy = point_event->ref_con_ly + moved_y; + + wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, + event->touch_id, sx, sy); +} + +static void handle_touch_up(struct sway_seat *seat, + struct wlr_touch_up_event *event) { + struct seatop_down_event *e = seat->seatop_data; + struct seatop_touch_point_event *point_event, *tmp; + + wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + wl_list_remove(&point_event->link); + free(point_event); + break; + } + } + + wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); + + if (wl_list_empty(&e->point_events)) { + seatop_begin_default(seat); + } +} + +static void handle_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly) { + struct seatop_down_event *e = seat->seatop_data; + double sx, sy; + struct wlr_surface *surface = NULL; + struct sway_node *focused_node = node_at_coords(seat, seat->touch_x, + seat->touch_y, &surface, &sx, &sy); + + if (!surface || surface != e->surface) { // Must start from the initial surface + return; + } + + struct seatop_touch_point_event *point_event = + calloc(1, sizeof(struct seatop_touch_point_event)); + if (!sway_assert(point_event, "Unable to allocate point_event")) { + return; + } + point_event->touch_id = event->touch_id; + point_event->ref_lx = lx; + point_event->ref_ly = ly; + point_event->ref_con_lx = sx; + point_event->ref_con_ly = sy; + + wl_list_insert(&e->point_events, &point_event->link); + + wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, + event->touch_id, sx, sy); + + if (focused_node) { + seat_set_focus(seat, focused_node); + } +} + +static void handle_touch_cancel(struct sway_seat *seat, + struct wlr_touch_cancel_event *event) { + struct seatop_down_event *e = seat->seatop_data; + struct seatop_touch_point_event *point_event, *tmp; + + wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + wl_list_remove(&point_event->link); + free(point_event); + break; + } + } + + if (e->surface) { + wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); + } + + if (wl_list_empty(&e->point_events)) { + seatop_begin_default(seat); + } +} + static void handle_pointer_axis(struct sway_seat *seat, struct wlr_pointer_axis_event *event) { struct sway_input_device *input_device = @@ -28,7 +137,7 @@ 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, - round(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source); } static void handle_button(struct sway_seat *seat, uint32_t time_msec, @@ -99,14 +208,18 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_axis = handle_pointer_axis, .tablet_tool_tip = handle_tablet_tool_tip, .tablet_tool_motion = handle_tablet_tool_motion, + .touch_motion = handle_touch_motion, + .touch_up = handle_touch_up, + .touch_down = handle_touch_down, + .touch_cancel = handle_touch_cancel, .unref = handle_unref, .end = handle_end, .allow_set_cursor = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, double sx, double sy) { - seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); + double sx, double sy) { + seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); struct seatop_down_event *e = seat->seatop_data; e->con = con; @@ -114,13 +227,20 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, transaction_commit_dirty(); } +void seatop_begin_touch_down(struct sway_seat *seat, + struct wlr_surface *surface, struct wlr_touch_down_event *event, + double sx, double sy, double lx, double ly) { + seatop_begin_down_on_surface(seat, surface, sx, sy); + handle_touch_down(seat, event, lx, ly); +} + void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { + struct wlr_surface *surface, double sx, double sy) { seatop_end(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); - if (!e) { + if (!sway_assert(e, "Unable to allocate e")) { return; } e->con = NULL; @@ -132,6 +252,7 @@ void seatop_begin_down_on_surface(struct sway_seat *seat, e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; + wl_list_init(&e->point_events); seat->seatop_impl = &seatop_impl; seat->seatop_data = e; diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 4e4db18c..bbed01a8 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -31,20 +31,19 @@ struct seatop_move_tiling_event { bool insert_after_target; }; -static void handle_render(struct sway_seat *seat, - struct sway_output *output, pixman_region32_t *damage) { +static void handle_render(struct sway_seat *seat, struct fx_render_context *ctx) { struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->threshold_reached) { return; } - if (e->target_node && node_get_output(e->target_node) == output) { + 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, output->wlr_output->scale); + scale_box(&box, ctx->output->wlr_output->scale); // Render blur pixman_region32_t opaque_region; @@ -52,11 +51,11 @@ static void handle_render(struct sway_seat *seat, struct decoration_data deco_data = get_undecorated_decoration_data(); deco_data.blur = e->con->blur_enabled; deco_data.corner_radius = e->con->corner_radius; - render_blur(false, output, damage, &box, &opaque_region, &deco_data, NULL); + struct wlr_fbox src_box = {0}; + render_blur(ctx, NULL, &src_box, &box, false, &opaque_region, deco_data); pixman_region32_fini(&opaque_region); - render_rounded_rect(output, damage, &box, color, - e->con->corner_radius * output->wlr_output->scale, ALL); + render_rounded_rect(ctx, &box, color, e->con->corner_radius * ctx->output->wlr_output->scale, ALL); } } diff --git a/sway/input/switch.c b/sway/input/switch.c index fc7dfaff..7a539801 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -1,6 +1,5 @@ #include "sway/config.h" #include "sway/input/switch.h" -#include #include "log.h" struct sway_switch *sway_switch_create(struct sway_seat *seat, diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 884eba74..902cb7ed 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -1,6 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include -#include +#include #include #include #include @@ -9,6 +9,10 @@ #include "sway/input/seat.h" #include "sway/input/tablet.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { struct sway_tablet_pad *pad = wl_container_of(listener, pad, tablet_destroy); @@ -54,15 +58,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) { tablet->seat_device->input_device->wlr_device; struct sway_seat *seat = tablet->seat_device->sway_seat; - if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { - seat_configure_xcursor(seat); - } + seat_configure_xcursor(seat); if (!tablet->tablet_v2) { tablet->tablet_v2 = wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); } +#if WLR_HAS_LIBINPUT_BACKEND /* Search for a sibling tablet pad */ if (!wlr_input_device_is_libinput(device)) { /* We can only do this on libinput devices */ @@ -87,6 +90,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) { break; } } +#endif } void sway_tablet_destroy(struct sway_tablet *tablet) { @@ -287,6 +291,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { tablet_pad->ring.notify = handle_tablet_pad_ring; wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); +#if WLR_HAS_LIBINPUT_BACKEND /* Search for a sibling tablet */ if (!wlr_input_device_is_libinput(wlr_device)) { /* We can only do this on libinput devices */ @@ -311,6 +316,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { break; } } +#endif } void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index eea4e186..d5866ef6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -3,7 +3,8 @@ #include #include #include -#include +#include +#include #include #include #include "config.h" @@ -21,6 +22,10 @@ #include "wlr-layer-shell-unstable-v1-protocol.h" #include "sway/desktop/idle_inhibit_v1.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static const int i3_output_id = INT32_MAX; static const int i3_scratch_id = INT32_MAX - 1; @@ -202,6 +207,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib return NULL; } +static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) { + switch (type) { + case WP_CONTENT_TYPE_V1_TYPE_NONE: + return "none"; + case WP_CONTENT_TYPE_V1_TYPE_PHOTO: + return "photo"; + case WP_CONTENT_TYPE_V1_TYPE_VIDEO: + return "video"; + case WP_CONTENT_TYPE_V1_TYPE_GAME: + return "game"; + } + return NULL; +} + json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); @@ -666,6 +685,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "idle_inhibitors", idle_inhibitors); + enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE; + if (c->view->surface != NULL) { + content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1, + c->view->surface); + } + if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) { + json_object_object_add(object, "content_type", + json_object_new_string(ipc_json_content_type_description(content_type))); + } + #if HAVE_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", @@ -886,6 +915,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } +#if WLR_HAS_LIBINPUT_BACKEND static json_object *describe_libinput_device(struct libinput_device *device) { json_object *object = json_object_new_object(); @@ -1053,6 +1083,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { uint32_t button = libinput_device_config_scroll_get_button(device); json_object_object_add(object, "scroll_button", json_object_new_int(button)); + const char *lock = "unknown"; + switch (libinput_device_config_scroll_get_button_lock(device)) { + case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: + lock = "enabled"; + break; + case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: + lock = "disabled"; + break; + } + json_object_object_add(object, "scroll_button_lock", + json_object_new_string(lock)); } } @@ -1096,6 +1137,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { return object; } +#endif json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { @@ -1159,12 +1201,14 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_double(scroll_factor)); } +#if WLR_HAS_LIBINPUT_BACKEND if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); json_object_object_add(object, "libinput", describe_libinput_device(libinput_dev)); } +#endif return object; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 00b01d7d..9692a77f 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -56,7 +56,6 @@ struct ipc_client { enum ipc_command_type pending_type; }; -struct sockaddr_un *ipc_user_sockaddr(void); int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); @@ -509,6 +508,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { json_object_put(json); } +void ipc_event_output(void) { + if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { + return; + } + sway_log(SWAY_DEBUG, "Sending output event"); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "change", json_object_new_string("unspecified")); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_OUTPUT); + json_object_put(json); +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -726,6 +739,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); if (strcmp(event_type, "workspace") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); + } else if (strcmp(event_type, "output") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); } else if (strcmp(event_type, "barconfig_update") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); } else if (strcmp(event_type, "bar_state_update") == 0) { @@ -910,7 +925,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt exit_cleanup: free(buf); - return; } bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, diff --git a/sway/lock.c b/sway/lock.c index e893622f..199624fc 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,10 +1,12 @@ #define _POSIX_C_SOURCE 200809L #include #include "log.h" +#include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/surface.h" struct sway_session_lock_surface { struct wlr_session_lock_surface_v1 *lock_surface; @@ -13,7 +15,6 @@ struct sway_session_lock_surface { struct wl_listener map; struct wl_listener destroy; struct wl_listener surface_commit; - struct wl_listener output_mode; struct wl_listener output_commit; struct wl_listener output_destroy; }; @@ -32,7 +33,8 @@ static void handle_surface_map(struct wl_listener *listener, void *data) { if (server.session_lock.focused == NULL) { set_lock_focused_surface(surf->surface); } - wlr_surface_send_enter(surf->surface, surf->output->wlr_output); + cursor_rebase_all(); + surface_enter_output(surf->surface, surf->output); output_damage_whole(surf->output); } @@ -41,16 +43,10 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { output_damage_surface(surf->output, 0, 0, surf->surface, false); } -static void handle_output_mode(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); -} - 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->committed & ( + if (event->state->committed & ( WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM)) { @@ -66,7 +62,7 @@ static void destroy_lock_surface(struct sway_session_lock_surface *surf) { struct wlr_session_lock_surface_v1 *other; wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { - if (other != surf->lock_surface && other->mapped) { + if (other != surf->lock_surface && other->surface->mapped) { next_focus = other->surface; break; } @@ -77,7 +73,6 @@ static void destroy_lock_surface(struct sway_session_lock_surface *surf) { wl_list_remove(&surf->map.link); wl_list_remove(&surf->destroy.link); wl_list_remove(&surf->surface_commit.link); - wl_list_remove(&surf->output_mode.link); wl_list_remove(&surf->output_commit.link); wl_list_remove(&surf->output_destroy.link); output_damage_whole(surf->output); @@ -111,13 +106,11 @@ static void handle_new_surface(struct wl_listener *listener, void *data) { surf->surface = lock_surface->surface; surf->output = output; surf->map.notify = handle_surface_map; - wl_signal_add(&lock_surface->events.map, &surf->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_mode.notify = handle_output_mode; - wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); 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; diff --git a/sway/meson.build b/sway/meson.build index f0f26346..40c7382e 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -15,11 +15,6 @@ sway_sources = files( 'xdg_decoration.c', 'desktop/desktop.c', - 'desktop/fx_renderer/fx_framebuffer.c', - 'desktop/fx_renderer/fx_renderer.c', - 'desktop/fx_renderer/fx_stencilbuffer.c', - 'desktop/fx_renderer/fx_texture.c', - 'desktop/fx_renderer/matrix.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', @@ -32,7 +27,6 @@ sway_sources = files( 'input/input-manager.c', 'input/cursor.c', 'input/keyboard.c', - 'input/libinput.c', 'input/seat.c', 'input/seatop_default.c', 'input/seatop_down.c', @@ -107,6 +101,7 @@ sway_sources = files( 'commands/nop.c', 'commands/output.c', 'commands/popup_during_fullscreen.c', + 'commands/primary_selection.c', 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', @@ -196,9 +191,11 @@ 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/repeat_delay.c', 'commands/input/repeat_rate.c', 'commands/input/scroll_button.c', + 'commands/input/scroll_button_lock.c', 'commands/input/scroll_factor.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', @@ -240,8 +237,6 @@ sway_sources = files( 'tree/output.c', ) -subdir('desktop/fx_renderer/shaders') - sway_deps = [ cairo, drm, @@ -252,19 +247,23 @@ sway_deps = [ math, pango, pcre2, - glesv2, pixman, + scenefx, threads, wayland_server, wlroots, xkbcommon, + xcb, xcb_icccm, egl, ] if have_xwayland sway_sources += 'desktop/xwayland.c' - sway_deps += xcb +endif + +if wlroots_features['libinput_backend'] + sway_sources += 'input/libinput.c' endif executable( diff --git a/sway/server.c b/sway/server.c index 262b7788..2b5b8d0f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -7,17 +8,17 @@ #include #include #include -#include #include #include #include #include +#include +#include #include -#include #include #include +#include #include -#include #include #include #include @@ -25,8 +26,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -41,16 +43,26 @@ #include "list.h" #include "log.h" #include "sway/config.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/input-manager.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/input/cursor.h" #include "sway/tree/root.h" + #if HAVE_XWAYLAND +#include #include "sway/xwayland.h" #endif +#if WLR_HAS_DRM_BACKEND +#include +#endif + +#define SWAY_XDG_SHELL_VERSION 2 +#define SWAY_LAYER_SHELL_VERSION 4 + +#if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { /* We only offer non-desktop outputs, but in the future we might want to do * more logic here. */ @@ -62,15 +74,67 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) { wlr_drm_lease_request_v1_reject(req); } } +#endif -#define SWAY_XDG_SHELL_VERSION 2 +static bool is_privileged(const struct wl_global *global) { +#if WLR_HAS_DRM_BACKEND + if (server.drm_lease_manager != NULL) { + struct wlr_drm_lease_device_v1 *drm_lease_dev; + wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { + if (drm_lease_dev->global == global) { + return true; + } + } + } +#endif + + return + global == server.output_manager_v1->global || + global == server.output_power_manager_v1->global || + global == server.input_method->global || + global == server.foreign_toplevel_manager->global || + global == server.data_control_manager_v1->global || + global == server.screencopy_manager_v1->global || + global == server.export_dmabuf_manager_v1->global || + global == server.security_context_manager_v1->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; +} + +static bool filter_global(const struct wl_client *client, + const struct wl_global *global, void *data) { +#if HAVE_XWAYLAND + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + if (xwayland && global == xwayland->shell_v1->global) { + return xwayland->server != NULL && client == xwayland->server->client; + } +#endif + + // Restrict usage of privileged protocols to unsandboxed clients + // TODO: add a way for users to configure an allow-list + const struct wlr_security_context_v1_state *security_context = + wlr_security_context_manager_v1_lookup_client( + server.security_context_manager_v1, (struct wl_client *)client); + if (is_privileged(global)) { + return security_context == NULL; + } + + return true; +} bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); server->wl_event_loop = wl_display_get_event_loop(server->wl_display); - server->backend = wlr_backend_autocreate(server->wl_display); + wl_display_set_global_filter(server->wl_display, filter_global, NULL); + + server->backend = wlr_backend_autocreate(server->wl_display, &server->session); if (!server->backend) { sway_log(SWAY_ERROR, "Unable to create backend"); return false; @@ -81,29 +145,29 @@ bool server_init(struct sway_server *server) { return false; } - server->wlr_renderer = wlr_renderer_autocreate(server->backend); - if (!server->wlr_renderer) { - sway_log(SWAY_ERROR, "Failed to create wlr_renderer"); + server->renderer = fx_renderer_create(server->backend); + if (!server->renderer) { + sway_log(SWAY_ERROR, "Failed to create fx_renderer"); return false; } - wlr_renderer_init_wl_shm(server->wlr_renderer, server->wl_display); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_dmabuf_texture_formats(server->wlr_renderer) != NULL) { - wlr_drm_create(server->wl_display, server->wlr_renderer); - server->linux_dmabuf_v1 = - wlr_linux_dmabuf_v1_create(server->wl_display, server->wlr_renderer); + if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { + wlr_drm_create(server->wl_display, server->renderer); + server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( + server->wl_display, 4, server->renderer); } server->allocator = wlr_allocator_autocreate(server->backend, - server->wlr_renderer); + server->renderer); if (!server->allocator) { sway_log(SWAY_ERROR, "Failed to create allocator"); return false; } - server->compositor = wlr_compositor_create(server->wl_display, - server->wlr_renderer); + 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); @@ -113,7 +177,11 @@ bool server_init(struct sway_server *server) { server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - wlr_gamma_control_manager_v1_create(server->wl_display); + server->gamma_control_manager_v1 = + wlr_gamma_control_manager_v1_create(server->wl_display); + server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; + wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, + &server->gamma_control_set_gamma); server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); @@ -123,12 +191,11 @@ bool server_init(struct sway_server *server) { wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); - server->idle = wlr_idle_create(server->wl_display); server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); - server->idle_inhibit_manager_v1 = - sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); + sway_idle_inhibit_manager_v1_init(); - server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); + server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, + SWAY_LAYER_SHELL_VERSION); wl_signal_add(&server->layer_shell->events.new_surface, &server->layer_shell_surface); server->layer_shell_surface.notify = handle_layer_shell_surface; @@ -193,6 +260,7 @@ bool server_init(struct sway_server *server) { sway_session_lock_init(); +#if WLR_HAS_DRM_BACKEND server->drm_lease_manager= wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); if (server->drm_lease_manager) { @@ -203,13 +271,17 @@ bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); sway_log(SWAY_INFO, "VR will not be available"); } +#endif - wlr_export_dmabuf_manager_v1_create(server->wl_display); - wlr_screencopy_manager_v1_create(server->wl_display); - wlr_data_control_manager_v1_create(server->wl_display); - wlr_primary_selection_v1_device_manager_create(server->wl_display); + server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); + server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); + server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + server->content_type_manager_v1 = + wlr_content_type_manager_v1_create(server->wl_display, 1); + wlr_fractional_scale_manager_v1_create(server->wl_display, 1); struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); @@ -221,6 +293,15 @@ bool server_init(struct sway_server *server) { xdg_activation_v1_handle_request_activate; wl_signal_add(&server->xdg_activation_v1->events.request_activate, &server->xdg_activation_v1_request_activate); + server->xdg_activation_v1_new_token.notify = + xdg_activation_v1_handle_new_token; + wl_signal_add(&server->xdg_activation_v1->events.new_token, + &server->xdg_activation_v1_new_token); + + struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = + wlr_cursor_shape_manager_v1_create(server->wl_display, 1); + server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; + wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); wl_list_init(&server->pending_launcher_ctxs); @@ -303,6 +384,10 @@ bool server_start(struct sway_server *server) { } #endif + if (config->primary_selection) { + wlr_primary_selection_v1_device_manager_create(server->wl_display); + } + sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", server->socket); if (!wlr_backend_start(server->backend)) { diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index e073c45d..442311bb 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -121,11 +121,16 @@ The following commands may only be used in the configuration file. *input* map_from_region Ignores inputs from this device that do not occur within the specified - region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 - (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful - if the device is not a keyboard and provides events in absolute terms (such - as a drawing tablet or touch screen - most pointers provide events relative - to the previous frame). + region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the + full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all + devices support millimeters. Only meaningful if the device is not a + keyboard and provides events in absolute terms (such as a drawing tablet + or touch screen - most pointers provide events relative to the previous + frame). + + Commonly used to maintain the aspect ratio of the input device and screen. + Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as + the argument. ## LIBINPUT CONFIGURATION @@ -175,12 +180,19 @@ The following commands may only be used in the configuration file. *input* pointer_accel [<-1|1>] Changes the pointer acceleration for the specified input device. +*input* rotation_angle + Sets the rotation angle of the device to the given clockwise angle in + degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive). + *input* scroll_button disable|button[1-3,8,9]| Sets the button used for scroll_method on_button_down. The button can be given as an event name or code, which can be obtained from *libinput debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to _disable_, it disables the scroll_method on_button_down. +*input* scroll_button_lock enabled|disabled + Enables or disables scroll button lock for specified input device. + *input* scroll_factor Changes the scroll factor for the specified input device. Scroll speed will be scaled by the given value, which must be non-negative. @@ -224,6 +236,8 @@ correct seat. absolute coordinates (with respect to the global coordinate space). Specifying either value as 0 will not update that coordinate. + Deprecated: use the virtual-pointer Wayland protocol instead. + *seat* cursor press|release button[1-9]| Simulate pressing (or releasing) the specified mouse button on the specified seat. The button can either be provided as a button event name or @@ -232,6 +246,8 @@ correct seat. event will be simulated, however _press_ and _release_ will be ignored and both will occur. + Deprecated: use the virtual-pointer Wayland protocol instead. + *seat* fallback true|false Set this seat as the fallback seat. A fallback seat will attach any device not explicitly attached to another seat (similar to a "default" seat). @@ -247,18 +263,16 @@ correct seat. If _when-typing_ is enabled, then the cursor will be hidden whenever a key is pressed. + Be aware that this setting can interfere with input handling in games and + certain types of software (Gimp, Blender etc) that rely on simultaneous + input from mouse and keyboard. + *seat* idle_inhibit Sets the set of input event sources which can prevent the seat from becoming idle, as a space separated list of source names. Valid names are "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", and "switch". The default behavior is to prevent idle on any event. -*seat* idle_wake - Sets the set of input event sources which can wake the seat from - its idle state, as a space separated list of source names. Valid names are - "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", - and "switch". The default behavior is to wake from idle on any event. - *seat* keyboard_grouping none|smart Set how the keyboards in the seat are grouped together. Currently, there are two options. _none_ will disable all keyboard grouping. This will make diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 2607c15c..bcfbccf8 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1229,6 +1229,9 @@ following properties will be included for devices that support them: : int : The scroll button to use when _scroll_method_ is _on_button_down_. This will be given as an input event code +|- scroll_button_lock +: string +: Whether scroll button lock is enabled. It can be _enabled_ or _disabled_ |- dwt : string : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ @@ -1480,6 +1483,9 @@ available: : workspace :[ Sent whenever an event involving a workspace occurs such as initialization of a new workspace or a different workspace gains focus +|- 0x80000001 +: output +: Sent when outputs are updated |- 0x80000002 : mode : Sent whenever the binding mode changes @@ -1600,6 +1606,20 @@ The following change types are currently available: } ``` +## 0x80000001. OUTPUT + +Sent whenever an output is added, removed, or its configuration is changed. +The event is a single object with the property _change_, which is a string +containing the reason for the change. Currently, the only value for _change_ is +_unspecified_. + +*Example Event:* +``` +{ + "change": "unspecified" +} +``` + ## 0x80000002. MODE Sent whenever the binding mode changes. The event consists of a single object diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b0f583f2..10ae1e0a 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -393,8 +393,8 @@ runtime. for_window move container to output *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ -[--to-code] [--input-device=] [--no-warn] [--no-repeat] [Group<1-4>+] \ - +[--to-code] [--input-device=] [--no-warn] [--no-repeat] [--inhibited] \ +[Group<1-4>+] Binds _key combo_ to execute the sway command _command_ when pressed. You may use XKB key names here (*wev*(1) is a good tool for discovering these). With the flag _--release_, the command is executed when the key combo is @@ -458,7 +458,8 @@ runtime. ``` *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ -[--locked] [--input-device=] [--no-warn] [Group<1-4>+] +[--locked] [--input-device=] [--no-warn] [--no-repeat] [--inhibited] \ +[Group<1-4>+] is also available for binding with key/button codes instead of key/button names. *bindswitch* [--locked] [--no-warn] [--reload] : @@ -724,7 +725,8 @@ The default colors are: while 1.1 (110%) is the default value. *default_border* normal|none|pixel [] - Set default border style for new tiled windows. + Set default border style for new tiled windows. Config reload won't affect + existing windows, only newly created ones after the reload. *default_floating_border* normal|none|pixel [] Set default border style for new floating windows. This only applies to @@ -908,6 +910,10 @@ The default colors are: dialog will not be rendered. If _leave_fullscreen_, the view will exit fullscreen mode and the dialog will be rendered. +*primary_selection* enabled|disabled + Enable or disable the primary selection clipboard. May only be configured + at launch. Default is _enabled_. + *set* $ Sets variable $_name_ to _value_. You can use the new variable in the arguments of future commands. When the variable is used, it can be escaped @@ -1063,6 +1069,9 @@ properties in practice for your applications. The following attributes may be matched with: +*all* + Matches all windows. + *app_id* Compare value against the app id. Can be a regular expression. If value is \_\_focused\_\_, then the app id must be the same as that of the currently diff --git a/sway/swaynag.c b/sway/swaynag.c index 4a0a6d30..6031174d 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -145,22 +145,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, va_list args; va_start(args, fmt); - size_t length = vsnprintf(NULL, 0, fmt, args) + 1; + char *str = vformat_str(fmt, args); va_end(args); - - char *temp = malloc(length + 1); - if (!temp) { + if (!str) { sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); return; } - va_start(args, fmt); - vsnprintf(temp, length, fmt, args); - va_end(args); + write(swaynag->fd[1], str, strlen(str)); - write(swaynag->fd[1], temp, length); - - free(temp); + free(str); } void swaynag_show(struct swaynag_instance *swaynag) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 9c1a11e5..af925d05 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -264,6 +264,9 @@ void arrange_workspace(struct sway_workspace *workspace) { area->width, area->height, area->x, area->y); bool first_arrange = workspace->width == 0 && workspace->height == 0; + struct wlr_box prev_box; + workspace_get_box(workspace, &prev_box); + double prev_x = workspace->x - workspace->current_gaps.left; double prev_y = workspace->y - workspace->current_gaps.top; workspace->width = area->width; @@ -277,13 +280,14 @@ void arrange_workspace(struct sway_workspace *workspace) { if (!first_arrange && (diff_x != 0 || diff_y != 0)) { for (int i = 0; i < workspace->floating->length; ++i) { struct sway_container *floater = workspace->floating->items[i]; - container_floating_translate(floater, diff_x, diff_y); - double center_x = floater->pending.x + floater->pending.width / 2; - double center_y = floater->pending.y + floater->pending.height / 2; struct wlr_box workspace_box; workspace_get_box(workspace, &workspace_box); - if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { - container_floating_move_to_center(floater); + floating_fix_coordinates(floater, &prev_box, &workspace_box); + // Set transformation for scratchpad windows. + if (floater->scratchpad) { + struct wlr_box output_box; + output_get_box(output, &output_box); + floater->transform = output_box; } } } diff --git a/sway/tree/container.c b/sway/tree/container.c index 2e61ff5c..9b90b774 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -3,14 +3,10 @@ #include #include #include -#include -#include -#include #include #include #include #include -#include #include "linux-dmabuf-unstable-v1-protocol.h" #include "cairo_util.h" #include "pango.h" @@ -22,6 +18,7 @@ #include "sway/ipc-server.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" @@ -392,17 +389,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent, } static bool surface_is_popup(struct wlr_surface *surface) { - while (!wlr_surface_is_xdg_surface(surface)) { - if (!wlr_surface_is_subsurface(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; } - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); surface = subsurface->parent; } struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; + 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, @@ -516,7 +513,6 @@ static void render_titlebar_text_texture(struct sway_output *output, cairo_t *c = cairo_create(dummy_surface); cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); cairo_font_options_t *fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); } else { @@ -721,6 +717,21 @@ void floating_calculate_constraints(int *min_width, int *max_width, } +void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { + if (!old->width || !old->height) { + // 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); + + 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); + + sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); + } +} + static void floating_natural_resize(struct sway_container *con) { int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, @@ -1034,6 +1045,13 @@ void container_floating_move_to(struct sway_container *con, workspace_add_floating(new_workspace, con); arrange_workspace(old_workspace); arrange_workspace(new_workspace); + // If the moved container was a visible scratchpad container, then + // update its transform. + if (con->scratchpad) { + struct wlr_box output_box; + output_get_box(new_output, &output_box); + con->transform = output_box; + } workspace_detect_urgent(old_workspace); workspace_detect_urgent(new_workspace); } @@ -1065,16 +1083,6 @@ void container_end_mouse_operation(struct sway_container *container) { } } -static bool devid_from_fd(int fd, dev_t *devid) { - struct stat stat; - if (fstat(fd, &stat) != 0) { - sway_log_errno(SWAY_ERROR, "fstat failed"); - return false; - } - *devid = stat.st_rdev; - return true; -} - static void set_fullscreen(struct sway_container *con, bool enable) { if (!con->view) { return; @@ -1101,60 +1109,19 @@ static void set_fullscreen(struct sway_container *con, bool enable) { } struct sway_output *output = con->pending.workspace->output; - struct wlr_output *wlr_output = output->wlr_output; - - // TODO: add wlroots helpers for all of this stuff - - const struct wlr_drm_format_set *renderer_formats = - wlr_renderer_get_dmabuf_texture_formats(server.wlr_renderer); - assert(renderer_formats); - - int renderer_drm_fd = wlr_renderer_get_drm_fd(server.wlr_renderer); - int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); - if (renderer_drm_fd < 0 || backend_drm_fd < 0) { - return; - } - - dev_t render_dev, scanout_dev; - if (!devid_from_fd(renderer_drm_fd, &render_dev) || - !devid_from_fd(backend_drm_fd, &scanout_dev)) { - return; - } - - const struct wlr_drm_format_set *output_formats = - wlr_output_get_primary_formats(output->wlr_output, - WLR_BUFFER_CAP_DMABUF); - if (!output_formats) { - return; - } - - struct wlr_drm_format_set scanout_formats = {0}; - if (!wlr_drm_format_set_intersect(&scanout_formats, - output_formats, renderer_formats)) { - return; - } - - struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { - { - .target_device = scanout_dev, - .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, - .formats = &scanout_formats, - }, - { - .target_device = render_dev, - .formats = renderer_formats, - }, + 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; + } - const struct wlr_linux_dmabuf_feedback_v1 feedback = { - .main_device = render_dev, - .tranches = tranches, - .tranches_len = sizeof(tranches) / sizeof(tranches[0]), - }; wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, con->view->surface, &feedback); - wlr_drm_format_set_finish(&scanout_formats); + wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static void container_fullscreen_workspace(struct sway_container *con) { @@ -1326,14 +1293,14 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { static void surface_send_enter_iterator(struct wlr_surface *surface, int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_enter(surface, wlr_output); + 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 wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_output); + struct sway_output *output = data; + surface_leave_output(surface, output); } void container_discover_outputs(struct sway_container *con) { @@ -1359,7 +1326,7 @@ void container_discover_outputs(struct sway_container *con) { 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->wlr_output); + surface_send_enter_iterator, output); if (con->view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_enter( con->view->foreign_toplevel, output->wlr_output); @@ -1371,7 +1338,7 @@ void container_discover_outputs(struct sway_container *con) { 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->wlr_output); + surface_send_leave_iterator, output); if (con->view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_leave( con->view->foreign_toplevel, output->wlr_output); @@ -1412,9 +1379,6 @@ list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; } - if (container_is_scratchpad_hidden(container)) { - return NULL; - } if (!container->pending.workspace) { return NULL; } @@ -1827,3 +1791,177 @@ int container_squash(struct sway_container *con) { } return change; } + +static void swap_places(struct sway_container *con1, + struct sway_container *con2) { + struct sway_container *temp = malloc(sizeof(struct sway_container)); + temp->pending.x = con1->pending.x; + temp->pending.y = con1->pending.y; + temp->pending.width = con1->pending.width; + temp->pending.height = con1->pending.height; + temp->width_fraction = con1->width_fraction; + temp->height_fraction = con1->height_fraction; + temp->pending.parent = con1->pending.parent; + temp->pending.workspace = con1->pending.workspace; + bool temp_floating = container_is_floating(con1); + + con1->pending.x = con2->pending.x; + con1->pending.y = con2->pending.y; + con1->pending.width = con2->pending.width; + con1->pending.height = con2->pending.height; + con1->width_fraction = con2->width_fraction; + con1->height_fraction = con2->height_fraction; + + con2->pending.x = temp->pending.x; + con2->pending.y = temp->pending.y; + con2->pending.width = temp->pending.width; + con2->pending.height = temp->pending.height; + con2->width_fraction = temp->width_fraction; + con2->height_fraction = temp->height_fraction; + + int temp_index = container_sibling_index(con1); + if (con2->pending.parent) { + container_insert_child(con2->pending.parent, con1, + container_sibling_index(con2)); + } else if (container_is_floating(con2)) { + workspace_add_floating(con2->pending.workspace, con1); + } else { + workspace_insert_tiling(con2->pending.workspace, con1, + container_sibling_index(con2)); + } + if (temp->pending.parent) { + container_insert_child(temp->pending.parent, con2, temp_index); + } else if (temp_floating) { + workspace_add_floating(temp->pending.workspace, con2); + } else { + workspace_insert_tiling(temp->pending.workspace, con2, temp_index); + } + + free(temp); +} + +static void swap_focus(struct sway_container *con1, + struct sway_container *con2, struct sway_seat *seat, + struct sway_container *focus) { + if (focus == con1 || focus == con2) { + struct sway_workspace *ws1 = con1->pending.workspace; + struct sway_workspace *ws2 = con2->pending.workspace; + enum sway_container_layout layout1 = container_parent_layout(con1); + enum sway_container_layout layout2 = container_parent_layout(con2); + if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { + if (workspace_is_visible(ws2)) { + seat_set_focus(seat, &con2->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); + } else if (focus == con2 && (layout1 == L_TABBED + || layout1 == L_STACKED)) { + if (workspace_is_visible(ws1)) { + seat_set_focus(seat, &con1->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); + } else if (ws1 != ws2) { + seat_set_focus_container(seat, focus == con1 ? con2 : con1); + } else { + seat_set_focus_container(seat, focus); + } + } else { + seat_set_focus_container(seat, focus); + } + + if (root->fullscreen_global) { + seat_set_focus(seat, + seat_get_focus_inactive(seat, &root->fullscreen_global->node)); + } +} + +void container_swap(struct sway_container *con1, struct sway_container *con2) { + if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { + return; + } + if (!sway_assert(!container_has_ancestor(con1, con2) + && !container_has_ancestor(con2, con1), + "Cannot swap ancestor and descendant")) { + return; + } + + sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", + con1->node.id, con2->node.id); + + bool scratch1 = con1->scratchpad; + bool hidden1 = container_is_scratchpad_hidden(con1); + bool scratch2 = con2->scratchpad; + bool hidden2 = container_is_scratchpad_hidden(con2); + if (scratch1) { + if (hidden1) { + root_scratchpad_show(con1); + } + root_scratchpad_remove_container(con1); + } + if (scratch2) { + if (hidden2) { + root_scratchpad_show(con2); + } + root_scratchpad_remove_container(con2); + } + + enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; + if (fs1) { + container_fullscreen_disable(con1); + } + enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; + if (fs2) { + container_fullscreen_disable(con2); + } + + struct sway_seat *seat = input_manager_current_seat(); + struct sway_container *focus = seat_get_focused_container(seat); + struct sway_workspace *vis1 = + output_get_active_workspace(con1->pending.workspace->output); + struct sway_workspace *vis2 = + output_get_active_workspace(con2->pending.workspace->output); + if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" + "workspace. This should not happen")) { + return; + } + + char *stored_prev_name = NULL; + if (seat->prev_workspace_name) { + stored_prev_name = strdup(seat->prev_workspace_name); + } + + swap_places(con1, con2); + + if (!workspace_is_visible(vis1)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); + } + if (!workspace_is_visible(vis2)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); + } + + swap_focus(con1, con2, seat, focus); + + if (stored_prev_name) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = stored_prev_name; + } + + if (scratch1) { + root_scratchpad_add_container(con2, NULL); + if (!hidden1) { + root_scratchpad_show(con2); + } + } + if (scratch2) { + root_scratchpad_add_container(con1, NULL); + if (!hidden2) { + root_scratchpad_show(con1); + } + } + + if (fs1) { + container_set_fullscreen(con2, fs1); + } + if (fs2) { + container_set_fullscreen(con1, fs2); + } +} diff --git a/sway/tree/output.c b/sway/tree/output.c index eccab2f7..4aa3a7fe 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -271,14 +271,13 @@ void output_disable(struct sway_output *output) { list_del(root->outputs, index); output->enabled = false; - output->current_mode = NULL; 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_inputs(); + 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 9df6f002..381e83de 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -50,6 +50,7 @@ struct sway_root *root_create(void) { 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); free(root); @@ -59,9 +60,8 @@ void root_destroy(struct sway_root *root) { static void root_scratchpad_set_minimize(struct sway_container *con, bool minimize) { if (con->view) { #if HAVE_XWAYLAND - if (wlr_surface_is_xwayland_surface(con->view->surface)) { - struct wlr_xwayland_surface *xsurface - = wlr_xwayland_surface_from_wlr_surface(con->view->surface); + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(con->view->surface))) { wlr_xwayland_surface_set_minimized(xsurface, minimize); return; } @@ -73,6 +73,16 @@ static void root_scratchpad_set_minimize(struct sway_container *con, bool minimi } } +static void set_container_transform(struct sway_workspace *ws, + struct sway_container *con) { + struct sway_output *output = ws->output; + struct wlr_box box = {0}; + if (output) { + output_get_box(output, &box); + } + con->transform = box; +} + void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { return; @@ -81,6 +91,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works struct sway_container *parent = con->pending.parent; struct sway_workspace *workspace = con->pending.workspace; + set_container_transform(workspace, con); + // Clear the fullscreen mode when sending to the scratchpad if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { container_fullscreen_disable(con); @@ -155,7 +167,10 @@ void root_scratchpad_show(struct sway_container *con) { // Show the container if (old_ws) { container_detach(con); - workspace_consider_destroy(old_ws); + // Make sure the last inactive container on the old workspace is above + // the workspace itself in the focus stack. + struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); + seat_set_raw_focus(seat, node); } else { // Act on the ancestor of scratchpad hidden split containers while (con->pending.parent) { @@ -169,18 +184,18 @@ void root_scratchpad_show(struct sway_container *con) { root_scratchpad_set_minimize(con, false); } - // Make sure the container's center point overlaps this workspace - double center_lx = con->pending.x + con->pending.width / 2; - double center_ly = con->pending.y + con->pending.height / 2; - - struct wlr_box workspace_box; - workspace_get_box(new_ws, &workspace_box); - if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { - container_floating_resize_and_center(con); + if (new_ws->output) { + struct wlr_box output_box; + output_get_box(new_ws->output, &output_box); + floating_fix_coordinates(con, &con->transform, &output_box); } + set_container_transform(new_ws, con); arrange_workspace(new_ws); seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); + if (old_ws) { + workspace_consider_destroy(old_ws); + } } static void disable_fullscreen(struct sway_container *con, void *data) { @@ -205,6 +220,8 @@ void root_scratchpad_hide(struct sway_container *con) { root_scratchpad_set_minimize(con, true); } + set_container_transform(con->pending.workspace, con); + disable_fullscreen(con, NULL); container_for_each_child(con, disable_fullscreen, NULL); container_detach(con); diff --git a/sway/tree/view.c b/sway/tree/view.c index 272967f4..bd2268fd 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ #include "sway/output.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -366,16 +367,15 @@ void view_set_activated(struct sway_view *view, bool activated) { } } -void view_request_activate(struct sway_view *view) { +void view_request_activate(struct sway_view *view, struct sway_seat *seat) { struct sway_workspace *ws = view->container->pending.workspace; - if (!ws) { // hidden scratchpad container - return; + if (!seat) { + seat = input_manager_current_seat(); } - struct sway_seat *seat = input_manager_current_seat(); switch (config->focus_on_window_activation) { case FOWA_SMART: - if (workspace_is_visible(ws)) { + if (ws && workspace_is_visible(ws)) { seat_set_focus_container(seat, view->container); container_raise_floating(view->container); } else { @@ -386,8 +386,12 @@ void view_request_activate(struct sway_view *view) { view_set_urgent(view, true); break; case FOWA_FOCUS: - seat_set_focus_container(seat, view->container); - container_raise_floating(view->container); + if (container_is_scratchpad_hidden_or_child(view->container)) { + root_scratchpad_show(view->container); + } else { + seat_set_focus_container(seat, view->container); + container_raise_floating(view->container); + } break; case FOWA_NONE: break; @@ -395,6 +399,12 @@ void view_request_activate(struct sway_view *view) { transaction_commit_dirty(); } +void view_request_urgent(struct sway_view *view) { + if (config->focus_on_window_activation != FOWA_NONE) { + view_set_urgent(view, true); + } +} + void view_set_csd_from_server(struct sway_view *view, bool enabled) { sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled); if (view->xdg_decoration) { @@ -526,7 +536,7 @@ static void view_populate_pid(struct sway_view *view) { #if HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND:; struct wlr_xwayland_surface *surf = - wlr_xwayland_surface_from_wlr_surface(view->surface); + wlr_xwayland_surface_try_from_wlr_surface(view->surface); pid = surf->pid; break; #endif @@ -885,9 +895,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, bool set_focus = should_focus(view); #if HAVE_XWAYLAND - if (wlr_surface_is_xwayland_surface(wlr_surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(wlr_surface); + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { set_focus &= wlr_xwayland_icccm_input_model(xsurface) != WLR_ICCCM_INPUT_MODEL_NONE; } @@ -911,6 +920,8 @@ void view_unmap(struct sway_view *view) { wl_list_remove(&view->surface_new_subsurface.link); + view->executed_criteria->length = 0; + if (view->urgent_timer) { wl_event_source_remove(view->urgent_timer); view->urgent_timer = NULL; @@ -984,7 +995,7 @@ static void subsurface_get_view_coords(struct sway_view_child *child, *sx = *sy = 0; } struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); + wlr_subsurface_try_from_wlr_surface(surface); *sx += subsurface->current.x; *sy += subsurface->current.y; } @@ -1179,7 +1190,7 @@ void view_child_init(struct sway_view_child *child, if (container != NULL) { struct sway_workspace *workspace = container->pending.workspace; if (workspace) { - wlr_surface_send_enter(child->surface, workspace->output->wlr_output); + surface_enter_output(child->surface, workspace->output); } } @@ -1220,33 +1231,21 @@ void view_child_destroy(struct sway_view_child *child) { } struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { - if (wlr_surface_is_xdg_surface(wlr_surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(wlr_surface); - if (xdg_surface == NULL) { - return NULL; - } + struct wlr_xdg_surface *xdg_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_surface_is_xwayland_surface(wlr_surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(wlr_surface); - if (xsurface == NULL) { - return NULL; - } + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xwayland_surface(xsurface); } #endif - if (wlr_surface_is_subsurface(wlr_surface)) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(wlr_surface); - if (subsurface == NULL) { - return NULL; - } + struct wlr_subsurface *subsurface; + if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_surface(subsurface->parent); } - if (wlr_surface_is_layer_surface(wlr_surface)) { + if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { return NULL; } @@ -1463,7 +1462,7 @@ static void view_save_buffer_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct sway_view *view = data; - if (surface && wlr_surface_has_buffer(surface)) { + 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; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index b2305341..98a177fa 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -178,22 +178,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { static bool workspace_valid_on_output(const char *output_name, const char *ws_name) { struct workspace_config *wsc = workspace_find_config(ws_name); - char identifier[128]; struct sway_output *output = output_by_name_or_id(output_name); if (!output) { return false; } - output_name = output->wlr_output->name; - output_get_identifier(identifier, sizeof(identifier), output); - if (!wsc) { return true; } for (int i = 0; i < wsc->outputs->length; i++) { - if (strcmp(wsc->outputs->items[i], "*") == 0 || - strcmp(wsc->outputs->items[i], output_name) == 0 || - strcmp(wsc->outputs->items[i], identifier) == 0) { + if (output_match_name_or_id(output, wsc->outputs->items[i])) { return true; } } @@ -288,13 +282,10 @@ char *workspace_next_name(const char *output_name) { // assignments primarily, falling back to bindings and numbers. struct sway_mode *mode = config->current_mode; - char identifier[128]; struct sway_output *output = output_by_name_or_id(output_name); if (!output) { return NULL; } - output_name = output->wlr_output->name; - output_get_identifier(identifier, sizeof(identifier), output); int order = INT_MAX; char *target = NULL; @@ -314,9 +305,7 @@ char *workspace_next_name(const char *output_name) { } bool found = false; for (int j = 0; j < wsc->outputs->length; ++j) { - if (strcmp(wsc->outputs->items[j], "*") == 0 || - strcmp(wsc->outputs->items[j], output_name) == 0 || - strcmp(wsc->outputs->items[j], identifier) == 0) { + if (output_match_name_or_id(output, wsc->outputs->items[j])) { found = true; free(target); target = strdup(wsc->workspace); @@ -656,15 +645,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, struct sway_output *workspace_output_get_highest_available( struct sway_workspace *ws, struct sway_output *exclude) { - char exclude_id[128] = {'\0'}; - if (exclude) { - output_get_identifier(exclude_id, sizeof(exclude_id), exclude); - } - for (int i = 0; i < ws->output_priority->length; i++) { - char *name = ws->output_priority->items[i]; - if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 - || strcmp(name, exclude_id) == 0)) { + const char *name = ws->output_priority->items[i]; + if (exclude && output_match_name_or_id(exclude, name)) { continue; } diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 2b94136c..3a035972 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,17 +1,14 @@ #include #include "sway/desktop/launcher.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, void *data) { const struct wlr_xdg_activation_v1_request_activate_event *event = data; - if (!wlr_surface_is_xdg_surface(event->surface)) { - return; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(event->surface); + wlr_xdg_surface_try_from_wlr_surface(event->surface); if (xdg_surface == NULL) { return; } @@ -20,11 +17,15 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } - if (!xdg_surface->mapped) { + struct launcher_ctx *ctx = event->token->data; + if (ctx == NULL) { + return; + } + + if (!xdg_surface->surface->mapped) { // This is a startup notification. If we are tracking it, the data // field is a launcher_ctx. - struct launcher_ctx *ctx = event->token->data; - if (!ctx || ctx->activated) { + if (ctx->activated) { // This ctx has already been activated and cannot be used again // for a startup notification. It will be destroyed return; @@ -35,5 +36,29 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } - view_request_activate(view); + // 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 (seat && ctx->had_focused_surface) { + view_request_activate(view, seat); + } else { + // The token is valid, but cannot be used to activate a window + view_request_urgent(view); + } +} + +void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { + struct wlr_xdg_activation_token_v1 *token = data; + struct sway_seat *seat = token->seat ? token->seat->data : + input_manager_current_seat(); + + struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (ws) { + launcher_ctx_create(token, &ws->node); + return; + } } diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index ec9e8d68..f7f5f5ed 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c @@ -53,7 +53,7 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, 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->surface->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) { @@ -79,7 +79,7 @@ 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->surface->surface == surface) { + if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { return deco; } } diff --git a/swaybar/bar.c b/swaybar/bar.c index 5e4ebd97..021fc3bd 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -362,6 +362,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { bar->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + bar->cursor_shape_manager = wl_registry_bind(registry, name, + &wp_cursor_shape_manager_v1_interface, 1); } } @@ -425,15 +428,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { // Second roundtrip for xdg-output wl_display_roundtrip(bar->display); - struct swaybar_seat *seat; - wl_list_for_each(seat, &bar->seats, link) { - struct swaybar_pointer *pointer = &seat->pointer; - if (!pointer) { - continue; + if (!bar->cursor_shape_manager) { + struct swaybar_seat *seat; + wl_list_for_each(seat, &bar->seats, link) { + struct swaybar_pointer *pointer = &seat->pointer; + if (!pointer) { + continue; + } + pointer->cursor_surface = + wl_compositor_create_surface(bar->compositor); + assert(pointer->cursor_surface); } - pointer->cursor_surface = - wl_compositor_create_surface(bar->compositor); - assert(pointer->cursor_surface); } if (bar->config->status_command) { diff --git a/swaybar/input.c b/swaybar/input.c index 8eccf542..ada4bc86 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) { int scale = pointer->current ? pointer->current->scale : 1; pointer->cursor_theme = wl_cursor_theme_load( cursor_theme, cursor_size * scale, seat->bar->shm); + if (!pointer->cursor_theme) { + sway_log(SWAY_ERROR, "Failed to load cursor theme"); + return; + } struct wl_cursor *cursor; - cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); + if (!cursor) { + sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); + return; + } pointer->cursor_image = cursor->images[0]; wl_surface_set_buffer_scale(pointer->cursor_surface, scale); wl_surface_attach(pointer->cursor_surface, @@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, struct swaybar_pointer *pointer = &seat->pointer; seat->pointer.x = wl_fixed_to_double(surface_x); seat->pointer.y = wl_fixed_to_double(surface_y); - pointer->serial = serial; + struct swaybar_output *output; wl_list_for_each(output, &seat->bar->outputs, link) { if (output->surface == surface) { @@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, break; } } - update_cursor(seat); + + if (seat->bar->cursor_shape_manager) { + struct wp_cursor_shape_device_v1 *device = + wp_cursor_shape_manager_v1_get_pointer( + seat->bar->cursor_shape_manager, wl_pointer); + wp_cursor_shape_device_v1_set_shape(device, serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy(device); + } else { + pointer->serial = serial; + update_cursor(seat); + } } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, @@ -207,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, } } - if (new) { + if (new && new != active) { ipc_send_workspace_command(bar, new->name); // Since we're asking Sway to switch to 'new', it should become visible. diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 9d81a9fb..33ae6544 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -426,12 +426,9 @@ bool ipc_initialize(struct swaybar *bar) { } free(res); - struct swaybar_config *config = bar->config; - char subscribe[128]; // suitably large buffer - len = snprintf(subscribe, 128, - "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", - config->binding_mode_indicator ? ", \"mode\"" : "", - config->workspace_buttons ? ", \"workspace\"" : ""); + char *subscribe = + "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]"; + len = strlen(subscribe); free(ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe, &len)); return true; diff --git a/swaybar/render.c b/swaybar/render.c index ccf36563..1113ca44 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -693,15 +693,6 @@ static uint32_t render_to_cairo(struct render_context *ctx) { struct swaybar_output *output = ctx->output; struct swaybar *bar = output->bar; struct swaybar_config *config = bar->config; - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - if (output->focused) { - ctx->background_color = config->colors.focused_background; - } else { - ctx->background_color = config->colors.background; - } - - cairo_set_source_u32(cairo, ctx->background_color); - cairo_paint(cairo); int th; get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); @@ -763,8 +754,17 @@ void render_frame(struct swaybar_output *output) { free_hotspots(&output->hotspots); + uint32_t background_color; + if (output->focused) { + background_color = output->bar->config->colors.focused_background; + } else { + background_color = output->bar->config->colors.background; + } + struct render_context ctx = { 0 }; ctx.output = output; + // initial background color used for deciding the best way to antialias text + ctx.background_color = background_color; cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); @@ -774,24 +774,23 @@ void render_frame(struct swaybar_output *output) { ctx.cairo = cairo; cairo_font_options_t *fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); ctx.textaa_safe = fo; if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { ctx.textaa_sharp = ctx.textaa_safe; } else { fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->subpixel)); ctx.textaa_sharp = fo; } - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, background_color); cairo_paint(cairo); - cairo_restore(cairo); + uint32_t height = render_to_cairo(&ctx); int config_height = output->bar->config->height; if (config_height > 0) { @@ -836,13 +835,15 @@ void render_frame(struct swaybar_output *output) { wl_surface_damage(output->surface, 0, 0, output->width, output->height); - uint32_t bg_alpha = ctx.background_color & 0xFF; + uint32_t bg_alpha = background_color & 0xFF; if (bg_alpha == 0xFF) { struct wl_region *region = wl_compositor_create_region(output->bar->compositor); wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); wl_surface_set_opaque_region(output->surface, region); wl_region_destroy(region); + } else { + wl_surface_set_opaque_region(output->surface, NULL); } struct wl_callback *frame_callback = wl_surface_frame(output->surface); diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index ddf2416d..eea2caa5 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -10,6 +10,7 @@ #include "swaybar/tray/tray.h" #include "list.h" #include "log.h" +#include "stringop.h" static const char *watcher_path = "/StatusNotifierWatcher"; @@ -138,12 +139,10 @@ static int handle_new_watcher(sd_bus_message *msg, bool init_host(struct swaybar_host *host, char *protocol, struct swaybar_tray *tray) { - size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; - host->watcher_interface = malloc(len); + host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol); if (!host->watcher_interface) { return false; } - snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, @@ -173,13 +172,10 @@ bool init_host(struct swaybar_host *host, char *protocol, } pid_t pid = getpid(); - size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", - protocol, pid) + 1; - host->service = malloc(service_len); + host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid); if (!host->service) { goto error; } - snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); ret = sd_bus_request_name(tray->bus, host->service, 0); if (ret < 0) { sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c426c3d4..b513dca5 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -40,9 +40,7 @@ static list_t *get_basedirs(void) { data_dirs = strdup(data_dirs); char *dir = strtok(data_dirs, ":"); do { - size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; - char *path = malloc(path_len); - snprintf(path, path_len, "%s/icons", dir); + char *path = format_str("%s/icons", dir); list_add(basedirs, path); } while ((dir = strtok(NULL, ":"))); free(data_dirs); @@ -206,13 +204,7 @@ static const char *entry_handler(char *group, char *key, char *value, */ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { // look for index.theme file - size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, - theme_name) + 1; - char *path = malloc(path_len); - if (!path) { - return NULL; - } - snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name); + char *path = format_str("%s/%s/index.theme", basedir, theme_name); FILE *theme_file = fopen(path, "r"); free(path); if (!theme_file) { @@ -416,26 +408,20 @@ static char *find_icon_in_subdir(char *name, char *basedir, char *theme, #endif }; - size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme, - subdir, name) + 1; - char *path = malloc(path_len); - for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { - snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, - name, extensions[i]); + char *path = format_str("%s/%s/%s/%s.%s", + basedir, theme, subdir, name, extensions[i]); if (access(path, R_OK) == 0) { return path; } + free(path); } - free(path); return NULL; } static bool theme_exists_in_basedir(char *theme, char *basedir) { - size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; - char *path = malloc(path_len); - snprintf(path, path_len, "%s/%s", basedir, theme); + char *path = format_str("%s/%s", basedir, theme); bool ret = dir_exists(path); free(path); return ret; diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 16afc27c..2458a8c2 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -6,6 +6,7 @@ #include #include "list.h" #include "log.h" +#include "stringop.h" #include "swaybar/tray/watcher.h" static const char *obj_path = "/StatusNotifierWatcher"; @@ -76,9 +77,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { service = service_or_path; path = "/StatusNotifierItem"; } - size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; - id = malloc(id_len); - snprintf(id, id_len, "%s%s", service, path); + id = format_str("%s%s", service, path); } if (list_seq_find(watcher->items, cmp_id, id) == -1) { @@ -107,7 +106,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); list_add(watcher->hosts, strdup(service)); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, - "StatusNotifierHostRegistered", "s", service); + "StatusNotifierHostRegistered", ""); } else { sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); } @@ -159,9 +158,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { return NULL; } - size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; - watcher->interface = malloc(len); - snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); + watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol); sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, diff --git a/swaymsg/main.c b/swaymsg/main.c index 237a4a71..f408307e 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -60,7 +60,7 @@ static void pretty_print_cmd(json_object *r) { if (!success_object(r)) { json_object *error; if (!json_object_object_get_ex(r, "error", &error)) { - printf("An unknkown error occurred"); + printf("An unknown error occurred"); } else { printf("Error: %s\n", json_object_get_string(error)); } @@ -185,11 +185,12 @@ static void pretty_print_seat(json_object *i) { } static void pretty_print_output(json_object *o) { - json_object *name, *rect, *focused, *active, *ws, *current_mode, *non_desktop; + json_object *name, *rect, *focused, *active, *power, *ws, *current_mode, *non_desktop; json_object_object_get_ex(o, "name", &name); json_object_object_get_ex(o, "rect", &rect); json_object_object_get_ex(o, "focused", &focused); json_object_object_get_ex(o, "active", &active); + json_object_object_get_ex(o, "power", &power); 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, @@ -226,6 +227,7 @@ static void pretty_print_output(json_object *o) { printf( "Output %s '%s %s %s'%s\n" " Current mode: %dx%d @ %.3f Hz\n" + " Power: %s\n" " Position: %d,%d\n" " Scale factor: %f\n" " Scale filter: %s\n" @@ -240,6 +242,7 @@ static void pretty_print_output(json_object *o) { json_object_get_int(width), json_object_get_int(height), (double)json_object_get_int(refresh) / 1000, + json_object_get_boolean(power) ? "on" : "off", json_object_get_int(x), json_object_get_int(y), json_object_get_double(scale), json_object_get_string(scale_filter), @@ -256,7 +259,7 @@ static void pretty_print_output(json_object *o) { json_object_get_string(adaptive_sync_status)); } else { printf( - "Output %s '%s %s %s' (inactive)\n", + "Output %s '%s %s %s' (disabled)\n", json_object_get_string(name), json_object_get_string(make), json_object_get_string(model), diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 24a9d6c9..abee1bb9 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -107,6 +107,8 @@ _swaymsg_ [options...] [message] Subscribe to a list of event types. The argument for this type should be provided in the form of a valid JSON array. If any of the types are invalid or if a valid JSON array is not provided, this will result in a failure. + For a list of valid event types and the data returned with them refer to + *sway-ipc*(7). # RETURN CODES diff --git a/swaynag/config.c b/swaynag/config.c index db86c1b8..b355b153 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -166,7 +166,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, fprintf(stderr, "Missing action for button %s\n", optarg); return EXIT_FAILURE; } - struct swaynag_button *button = calloc(sizeof(struct swaynag_button), 1); + struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button)); if (!button) { perror("calloc"); return EXIT_FAILURE; @@ -226,10 +226,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; case 'f': // Font if (type) { - free(type->font); pango_font_description_free(type->font_description); - type->font = strdup(optarg); - type->font_description = pango_font_description_from_string(type->font); + type->font_description = pango_font_description_from_string(optarg); } break; case 'l': // Detailed Message @@ -245,8 +243,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; case 'L': // Detailed Button Text if (swaynag) { - free(swaynag->details.button_details.text); - swaynag->details.button_details.text = strdup(optarg); + free(swaynag->details.details_text); + swaynag->details.details_text = strdup(optarg); } break; case 'm': // Message diff --git a/swaynag/main.c b/swaynag/main.c index 2ce37831..20390207 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -29,10 +29,12 @@ int main(int argc, char **argv) { wl_list_init(&swaynag.outputs); wl_list_init(&swaynag.seats); - struct swaynag_button button_close = { 0 }; - button_close.text = strdup("X"); - button_close.type = SWAYNAG_ACTION_DISMISS; - list_add(swaynag.buttons, &button_close); + struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button)); + button_close->text = strdup("X"); + button_close->type = SWAYNAG_ACTION_DISMISS; + list_add(swaynag.buttons, button_close); + + swaynag.details.details_text = strdup("Toggle details"); char *config_path = NULL; bool debug = false; @@ -54,8 +56,6 @@ int main(int argc, char **argv) { } } - swaynag.details.button_details.text = strdup("Toggle details"); - swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; if (argc > 1) { struct swaynag_type *type_args = swaynag_type_new(""); @@ -88,17 +88,20 @@ int main(int argc, char **argv) { swaynag_type_merge(type, swaynag_type_get(types, "")); swaynag.type = type; - swaynag_types_free(types); - if (swaynag.details.message) { - list_add(swaynag.buttons, &swaynag.details.button_details); + swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button)); + swaynag.details.button_details->text = strdup(swaynag.details.details_text); + swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; + list_add(swaynag.buttons, swaynag.details.button_details); } sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); - sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); + char *font = pango_font_description_to_string(swaynag.type->font_description); + sway_log(SWAY_DEBUG, "Font: %s", font); + free(font); sway_log(SWAY_DEBUG, "Buttons"); for (int i = 0; i < swaynag.buttons->length; i++) { struct swaynag_button *button = swaynag.buttons->items[i]; @@ -109,11 +112,9 @@ int main(int argc, char **argv) { swaynag_setup(&swaynag); swaynag_run(&swaynag); - return status; cleanup: swaynag_types_free(types); - free(swaynag.details.button_details.text); swaynag_destroy(&swaynag); return status; } diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 5620155d..6ea739e3 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -153,8 +153,15 @@ static void update_cursor(struct swaynag_seat *seat) { } pointer->cursor_theme = wl_cursor_theme_load( cursor_theme, cursor_size * swaynag->scale, swaynag->shm); - struct wl_cursor *cursor = - wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + if (!pointer->cursor_theme) { + sway_log(SWAY_ERROR, "Failed to load cursor theme"); + return; + } + struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); + if (!cursor) { + sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); + return; + } pointer->cursor_image = cursor->images[0]; wl_surface_set_buffer_scale(pointer->cursor_surface, swaynag->scale); @@ -182,11 +189,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct swaynag_seat *seat = data; + struct swaynag_pointer *pointer = &seat->pointer; pointer->x = wl_fixed_to_int(surface_x); pointer->y = wl_fixed_to_int(surface_y); - pointer->serial = serial; - update_cursor(seat); + + if (seat->swaynag->cursor_shape_manager) { + struct wp_cursor_shape_device_v1 *device = + wp_cursor_shape_manager_v1_get_pointer( + seat->swaynag->cursor_shape_manager, wl_pointer); + wp_cursor_shape_device_v1_set_shape(device, serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy(device); + } else { + pointer->serial = serial; + update_cursor(seat); + } } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, @@ -378,6 +396,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { swaynag->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + swaynag->cursor_shape_manager = wl_registry_bind( + registry, name, &wp_cursor_shape_manager_v1_interface, 1); } } @@ -456,7 +477,9 @@ void swaynag_setup(struct swaynag *swaynag) { exit(EXIT_FAILURE); } - swaynag_setup_cursors(swaynag); + if (!swaynag->cursor_shape_manager) { + swaynag_setup_cursors(swaynag); + } swaynag->surface = wl_compositor_create_surface(swaynag->compositor); assert(swaynag->surface); @@ -483,10 +506,6 @@ void swaynag_run(struct swaynag *swaynag) { && wl_display_dispatch(swaynag->display) != -1) { // This is intentionally left blank } - - if (swaynag->display) { - wl_display_disconnect(swaynag->display); - } } void swaynag_destroy(struct swaynag *swaynag) { @@ -501,6 +520,7 @@ void swaynag_destroy(struct swaynag *swaynag) { } list_free(swaynag->buttons); free(swaynag->details.message); + free(swaynag->details.details_text); free(swaynag->details.button_up.text); free(swaynag->details.button_down.text); @@ -541,4 +561,8 @@ void swaynag_destroy(struct swaynag *swaynag) { if (swaynag->shm) { wl_shm_destroy(swaynag->shm); } + + if (swaynag->display) { + wl_display_disconnect(swaynag->display); + } } diff --git a/swaynag/types.c b/swaynag/types.c index a46aacd5..409cc668 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -32,9 +32,8 @@ struct swaynag_type *swaynag_type_new(const char *name) { void swaynag_types_add_default(list_t *types) { struct swaynag_type *type_defaults = swaynag_type_new(""); - type_defaults->font = strdup("pango:Monospace 10"); type_defaults->font_description = - pango_font_description_from_string(type_defaults->font); + pango_font_description_from_string("pango:Monospace 10"); type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; @@ -92,10 +91,6 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { return; } - if (src->font) { - dest->font = strdup(src->font); - } - if (src->font_description) { dest->font_description = pango_font_description_copy(src->font_description); } @@ -178,7 +173,6 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { void swaynag_type_free(struct swaynag_type *type) { free(type->name); - free(type->font); pango_font_description_free(type->font_description); free(type->output); free(type); From 62472238caa0cba06e8186e3adfb36ade65e6ce3 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Mon, 15 Apr 2024 01:43:19 -0400 Subject: [PATCH 10/18] added git only message for shadow offset --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f14e31aa..0376baf0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Sway is an incredible window manager, and certainly one of the most well establi - `shadows_on_csd enable|disable` (**Note**: The shadow might not fit some windows) - `shadow_blur_radius ` - `shadow_color ex, #0000007F` - - `shadow_offset ` + - `shadow_offset ` (**Note**: git only) - `shadow_inactive_color ex, #0000007F` + LayerShell effects (to blur panels / notifications etc): - `layer_effects ` From a4d51d55e5236ac9fc1b9be64156388d94c09c3e Mon Sep 17 00:00:00 2001 From: ozwaldorf Date: Tue, 16 Apr 2024 05:10:19 -0400 Subject: [PATCH 11/18] fix: update sway original version (#289) --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a8142519..2ab72e13 100644 --- a/meson.build +++ b/meson.build @@ -187,7 +187,7 @@ if git.found() endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -add_project_arguments('-DSWAY_ORIGINAL_VERSION="1.8.1"', language: 'c') +add_project_arguments('-DSWAY_ORIGINAL_VERSION="1.9.0"', language: 'c') # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') From 8ad2605c65d85ed1821dbb970fe825f676604437 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:29:15 +0200 Subject: [PATCH 12/18] Fixed compilation errors (#290) --- sway/desktop/render.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index cbf99177..34786982 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1593,7 +1593,6 @@ void output_render(struct fx_render_context *ctx) { pixman_region32_t *damage = ctx->output_damage; struct fx_effect_framebuffers *effect_fbos = ctx->pass->fx_effect_framebuffers; - struct fx_renderer *renderer = fx_get_renderer(ctx->renderer); struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -1737,15 +1736,15 @@ void output_render(struct fx_render_context *ctx) { 0, 0, output_width, output_height); // capture the padding pixels around the blur where artifacts will be drawn - pixman_region32_subtract(&renderer->blur_padding_region, + 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); // Capture the padding pixels before blur for later use - fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, - ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, + fx_renderer_read_to_buffer(ctx->pass, &effect_fbos->blur_padding_region, + effect_fbos->blur_saved_pixels_buffer, ctx->pass->buffer, true); } } @@ -1831,9 +1830,8 @@ renderer_end: // Not needed if we damaged the whole viewport if (!effect_fbos->blur_buffer_dirty) { // Render the saved pixels over the blur artifacts - fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, - ctx->pass->buffer, - ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, true); + 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); From 40a5ebf109ead3f0d9c2fc3ab11a2a62d8ab3fba Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Thu, 18 Apr 2024 01:03:25 +0200 Subject: [PATCH 13/18] Fix segfaults (#291) * Add NULL checks for effect_fbos * Fix freeing undefined pixman region --- sway/desktop/render.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 34786982..65bf2062 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1592,6 +1592,9 @@ void output_render(struct fx_render_context *ctx) { struct sway_output *output = ctx->output; pixman_region32_t *damage = ctx->output_damage; + pixman_region32_t transformed_damage; + pixman_region32_init(&transformed_damage); + struct fx_effect_framebuffers *effect_fbos = ctx->pass->fx_effect_framebuffers; struct sway_workspace *workspace = output->current.active_workspace; @@ -1617,8 +1620,6 @@ void output_render(struct fx_render_context *ctx) { }, }); } - pixman_region32_t transformed_damage; - pixman_region32_init(&transformed_damage); pixman_region32_copy(&transformed_damage, damage); transform_output_damage(&transformed_damage, wlr_output); @@ -1704,7 +1705,7 @@ void output_render(struct fx_render_context *ctx) { pixman_region32_init(&blur_region); bool workspace_has_blur = workspace_get_blur_info(workspace, &blur_region); // Expand the damage to compensate for blur - if (workspace_has_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 @@ -1765,7 +1766,7 @@ void output_render(struct fx_render_context *ctx) { // Check if the background needs to be blurred. // Render optimized/x-ray blur - if (workspace_has_blur && effect_fbos->blur_buffer_dirty) { + 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 = { @@ -1828,7 +1829,7 @@ render_overlay: renderer_end: // Not needed if we damaged the whole viewport - if (!effect_fbos->blur_buffer_dirty) { + 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); From 4b954d5a05d986afcaaf488e744c5566a2d031ef Mon Sep 17 00:00:00 2001 From: ozwaldorf Date: Thu, 18 Apr 2024 23:51:51 -0400 Subject: [PATCH 14/18] flake: update for swayfx 0.4 (#284) --- flake.lock | 43 ++++++++++++++++++++--- flake.nix | 101 +++++++++++++++++++++++++++++------------------------ 2 files changed, 94 insertions(+), 50 deletions(-) diff --git a/flake.lock b/flake.lock index e99f60b5..e383db5a 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,27 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1712192574, - "narHash": "sha256-LbbVOliJKTF4Zl2b9salumvdMXuQBr2kuKP5+ZwbYq4=", + "lastModified": 1713349283, + "narHash": "sha256-2bjFu3+1zPWZPPGqF+7rumTvEwmdBHBhjPva/AMSruQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f480f9d09e4b4cf87ee6151eba068197125714de", + "rev": "2e359fb3162c85095409071d131e08252d91a14f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1713128889, + "narHash": "sha256-aB90ZqzosyRDpBh+rILIcyP5lao8SKz8Sr2PSWvZrzk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "2748d22b45a99fb2deafa5f11c7531c212b2cefa", "type": "github" }, "original": { @@ -18,7 +34,26 @@ }, "root": { "inputs": { - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "scenefx": "scenefx" + } + }, + "scenefx": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1713495445, + "narHash": "sha256-dMvGkhjt72NznwI57HLR+Oc6QMctf16W4zI1XYuwnZI=", + "owner": "wlrfx", + "repo": "scenefx", + "rev": "5ada125a56012923c47fcf3d049fab32eb7104ff", + "type": "github" + }, + "original": { + "owner": "wlrfx", + "repo": "scenefx", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index ab09cb94..21314d67 100644 --- a/flake.nix +++ b/flake.nix @@ -1,66 +1,75 @@ { description = "Swayfx development environment"; - inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + scenefx.url = "github:wlrfx/scenefx"; + }; outputs = - { self, nixpkgs, ... }: + { + self, + nixpkgs, + scenefx, + ... + }: let - pkgsFor = system: import nixpkgs { inherit system; }; + mkPackage = pkgs: { + swayfx-unwrapped = + (pkgs.swayfx-unwrapped.override { wlroots_0_16 = pkgs.wlroots_0_17; }).overrideAttrs + (old: { + version = "0.4.0-git"; + src = pkgs.lib.cleanSource ./.; + nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.cmake ]; + buildInputs = old.buildInputs ++ [ pkgs.scenefx ]; + }); + }; + targetSystems = [ "aarch64-linux" "x86_64-linux" ]; - mkPackage = pkgs: { - swayfx-unwrapped = - (pkgs.swayfx-unwrapped.override { - # When the sway 1.9 rebase is finished, this will need to be overridden. - # wlroots_0_16 = pkgs.wlroots_0_16; - }).overrideAttrs - (old: { - version = "0.3.2-git"; - src = pkgs.lib.cleanSource ./.; - }); - }; + pkgsFor = + system: + import nixpkgs { + inherit system; + overlays = [ scenefx.overlays.insert ]; + }; + forEachSystem = f: nixpkgs.lib.genAttrs targetSystems (system: f (pkgsFor system)); in { overlays = rec { default = insert; - # Override onto the input nixpkgs - override = _: prev: mkPackage prev; - # Insert using the locked nixpkgs + # Insert using the locked nixpkgs. Can be used with any nixpkgs version. insert = _: prev: mkPackage (pkgsFor prev.system); + # Override onto the input nixpkgs. Users *MUST* have a scenefx overlay + # used before this overlay, otherwise pkgs.scenefx will be unavailable + override = _: prev: mkPackage prev; }; - packages = nixpkgs.lib.genAttrs targetSystems ( - system: (mkPackage (pkgsFor system) // { default = self.packages.${system}.swayfx-unwrapped; }) + packages = forEachSystem ( + pkgs: (mkPackage pkgs // { default = self.packages.${pkgs.system}.swayfx-unwrapped; }) ); - devShells = nixpkgs.lib.genAttrs targetSystems ( - system: - let - pkgs = pkgsFor system; - in - { - default = pkgs.mkShell { - name = "swayfx-shell"; - inputsFrom = [ - self.packages.${system}.swayfx-unwrapped - pkgs.wlroots_0_17 - ]; - nativeBuildInputs = with pkgs; [ - cmake - wayland-scanner - hwdata # for wlroots - ]; - # Copy the nix version of wlroots into the project - shellHook = with pkgs; '' - ( - mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects" - cp -R --no-preserve=mode,ownership ${wlroots_0_17.src} wlroots - )''; - }; - } - ); + devShells = forEachSystem (pkgs: { + default = pkgs.mkShell { + name = "swayfx-shell"; + inputsFrom = [ + self.packages.${pkgs.system}.swayfx-unwrapped + pkgs.wlroots_0_17 + pkgs.scenefx + ]; + packages = with pkgs; [ + gdb # for debugging + ]; + shellHook = '' + ( + # Copy the nix version of wlroots and scenefx into the project + mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects" + cp -R --no-preserve=mode,ownership ${pkgs.wlroots_0_17.src} wlroots + cp -R --no-preserve=mode,ownership ${pkgs.scenefx.src} scenefx + )''; + }; + }); - formatter = nixpkgs.lib.genAttrs targetSystems (system: (pkgsFor system).nixfmt-rfc-style); + formatter = forEachSystem (pkgs: pkgs.nixfmt-rfc-style); }; } From a5a69d4d4e9a8fe306e27ca46cde3a8b9d312ae3 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:41:51 +0200 Subject: [PATCH 15/18] fix: gray background rect not being expanded by blur size (#295) --- sway/desktop/render.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 65bf2062..30f1c541 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1592,9 +1592,6 @@ void output_render(struct fx_render_context *ctx) { struct sway_output *output = ctx->output; pixman_region32_t *damage = ctx->output_damage; - pixman_region32_t transformed_damage; - pixman_region32_init(&transformed_damage); - struct fx_effect_framebuffers *effect_fbos = ctx->pass->fx_effect_framebuffers; struct sway_workspace *workspace = output->current.active_workspace; @@ -1609,7 +1606,7 @@ void output_render(struct fx_render_context *ctx) { if (!pixman_region32_not_empty(damage)) { // Output isn't damaged but needs buffer swap - goto renderer_end; + return; } if (debug.damage == DAMAGE_HIGHLIGHT) { @@ -1620,6 +1617,9 @@ void output_render(struct fx_render_context *ctx) { }, }); } + + pixman_region32_t transformed_damage; + pixman_region32_init(&transformed_damage); pixman_region32_copy(&transformed_damage, damage); transform_output_damage(&transformed_damage, wlr_output); @@ -1715,17 +1715,9 @@ void output_render(struct fx_render_context *ctx) { // copy the surrounding content where the blur would display artifacts // and draw it above the artifacts - // ensure that the damage isn't expanding past the output's size - int32_t damage_width = damage->extents.x2 - damage->extents.x1; - int32_t damage_height = damage->extents.y2 - damage->extents.y1; - if (damage_width > output_width || damage_height > output_height) { - pixman_region32_intersect_rect(damage, damage, - 0, 0, output_width, output_height); - } else { - // 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()); - } + // 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); @@ -1743,6 +1735,10 @@ void output_render(struct fx_render_context *ctx) { 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, From 80c0c506730fa05ab86555219be177dad14eb5c6 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Mon, 29 Apr 2024 01:32:54 -0400 Subject: [PATCH 16/18] removed old publish script, updated to 0.4 --- .github/workflows/AUR_publish.yml | 56 ------------------------------- meson.build | 2 +- 2 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 .github/workflows/AUR_publish.yml diff --git a/.github/workflows/AUR_publish.yml b/.github/workflows/AUR_publish.yml deleted file mode 100644 index 2b768c5a..00000000 --- a/.github/workflows/AUR_publish.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Package for the AUR - -on: - release: - types: [published] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: -jobs: - aur-publish-stable: - runs-on: ubuntu-latest - steps: - - name: Check out sources - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Publish AUR Stable package - uses: ulises-jeremias/github-actions-aur-publish@v1 - with: - pkgname: swayfx - pkgbuild: ./build-scripts/aur/PKGBUILD - assets: | - ./build-scripts/aur/sway-portals.conf - ./build-scripts/aur/50-systemd-user.conf - ./build-scripts/aur/sway.install - commit_username: ${{ secrets.AUR_USERNAME }} - commit_email: ${{ secrets.AUR_EMAIL }} - ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} - commit_message: Update AUR package - ssh_keyscan_types: rsa,dsa,ecdsa,ed25519 - update_pkgver: false - - aur-publish-git: - runs-on: ubuntu-latest - steps: - - name: Check out sources - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Publish AUR -git package - uses: ulises-jeremias/github-actions-aur-publish@v1 - with: - pkgname: swayfx-git - pkgbuild: ./build-scripts/aur-git/PKGBUILD - assets: | - ./build-scripts/aur/sway-portals.conf - ./build-scripts/aur-git/50-systemd-user.conf - ./build-scripts/aur-git/sway.install - commit_username: ${{ secrets.AUR_USERNAME }} - commit_email: ${{ secrets.AUR_EMAIL }} - ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} - commit_message: Update AUR package - ssh_keyscan_types: rsa,dsa,ecdsa,ed25519 - update_pkgver: false diff --git a/meson.build b/meson.build index 2ab72e13..d5086804 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '0.3.3', + version: '0.4', license: 'MIT', meson_version: '>=0.60.0', default_options: [ From c6b4411b86b76e45b8ba23e92697325f182564f2 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Mon, 29 Apr 2024 02:09:52 -0400 Subject: [PATCH 17/18] Removed git only messages --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0376baf0..2a5d4033 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,17 @@ Sway is an incredible window manager, and certainly one of the most well establi - `blur_xray enable|disable`: this will set floating windows to blur based on the background, not the windows below. You probably want to set this to `disable` :) - `blur_passes ` - `blur_radius ` - - `blur_noise ` (**Note**: git only, percentage of noise to add) - - `blur_brightness ` (**Note**: git only, percentage of original brightness to adjust) - - `blur_contrast ` (**Note**: git only, percentage of original contrast to adjust) - - `blur_saturation ` (**Note**: git only, percentage of original saturation to adjust) + - `blur_noise ` + - `blur_brightness ` + - `blur_contrast ` + - `blur_saturation ` + Corner radius: `corner_radius ` + Window shadows: - `shadows enable|disable` - `shadows_on_csd enable|disable` (**Note**: The shadow might not fit some windows) - `shadow_blur_radius ` - `shadow_color ex, #0000007F` - - `shadow_offset ` (**Note**: git only) + - `shadow_offset ` - `shadow_inactive_color ex, #0000007F` + LayerShell effects (to blur panels / notifications etc): - `layer_effects ` From e45b420ccc888f4d14782f233cf037941a2425db Mon Sep 17 00:00:00 2001 From: Eric Chastain Date: Tue, 30 Apr 2024 23:38:49 -0400 Subject: [PATCH 18/18] Update server.c removing duplicated code block (#306) --- sway/server.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sway/server.c b/sway/server.c index 2b5b8d0f..c4b98d22 100644 --- a/sway/server.c +++ b/sway/server.c @@ -140,11 +140,6 @@ bool server_init(struct sway_server *server) { return false; } - if (!server->backend) { - sway_log(SWAY_ERROR, "Unable to create backend"); - return false; - } - server->renderer = fx_renderer_create(server->backend); if (!server->renderer) { sway_log(SWAY_ERROR, "Failed to create fx_renderer");