diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 90bb9286..b6582979 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -9,12 +9,23 @@ body: --- - - type: input + - type: textarea id: ver attributes: label: Hyprland Version - description: "Paste here the output of `hyprctl version`." - placeholder: Hyprland, built from branch main at commit... + description: "Paste here the output of `hyprctl version`. For hyprland after 0.34.0, paste `hyprctl systeminfo` instead." + value: "
+ System/Version info + + + ```sh + + + + ``` + + +
" validations: required: true diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml index 2985ce9a..8a33dd55 100644 --- a/.github/actions/setup_base/action.yml +++ b/.github/actions/setup_base/action.yml @@ -23,6 +23,7 @@ runs: glm \ glslang \ go \ + hyprlang \ jq \ libc++ \ libdisplay-info \ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a78b7522..01bb43d1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,29 +8,20 @@ jobs: container: image: archlinux steps: - - name: Get required pacman pkgs - run: | - sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff - - name: Set up user - run: | - useradd -m githubuser - echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers - - name: Install libdisplay-info from the AUR - run: | - su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar" - - name: Fix permissions for git - run: | - git config --global --add safe.directory /__w/Hyprland/Hyprland - - name: Checkout Hyprland - uses: actions/checkout@v3 + - name: Checkout repository actions + uses: actions/checkout@v4 with: - submodules: recursive + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + with: + INSTALL_XORG_PKGS: true + - name: Build Hyprland run: | - git submodule sync --recursive && git submodule update --init --force --recursive make all + - name: Compress and package artifacts run: | mkdir x86_64-pc-linux-gnu @@ -45,6 +36,7 @@ jobs: cp -r example/ hyprland/ cp -r assets/ hyprland/ tar -cvf Hyprland.tar.xz hyprland + - name: Release uses: actions/upload-artifact@v3 with: @@ -57,28 +49,19 @@ jobs: container: image: archlinux steps: - - name: Download dependencies - run: | - sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd cmake jq python libliftoff - - name: Set up user - run: | - useradd -m githubuser - echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers - - name: Install libdisplay-info from the AUR - run: | - su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar" - - name: Checkout Hyprland - uses: actions/checkout@v3 + - name: Checkout repository actions + uses: actions/checkout@v4 with: - submodules: true + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + - name: Configure - run: | - meson obj-x86_64-pc-linux-gnu \ - -Ddefault_library=static + run: meson setup build -Ddefault_library=static + - name: Compile - run: ninja -C obj-x86_64-pc-linux-gnu + run: ninja -C build noxwayland: name: "Build Hyprland in pure Wayland (Arch)" @@ -86,23 +69,36 @@ jobs: container: image: archlinux steps: - - name: Download dependencies - run: | - sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd libliftoff - - name: Set up user - run: | - useradd -m githubuser - echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers - - name: Install libdisplay-info from the AUR - run: | - su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar" - - name: Checkout Hyprland - uses: actions/checkout@v3 + - name: Checkout repository actions + uses: actions/checkout@v4 with: - submodules: true + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + - name: Configure - run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja + run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja + - name: Compile run: make release + + clang-format: + name: "Code Style (Arch)" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + + - name: Configure + run: meson setup build -Ddefault_library=static + + - name: clang-format check + run: ninja -C build clang-format-check diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml index a1ea0f24..2c05d5e2 100644 --- a/.github/workflows/nix-build.yml +++ b/.github/workflows/nix-build.yml @@ -26,4 +26,4 @@ jobs: name: hyprland authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - run: nix build -L ${{ matrix.command }} + - run: nix build .#${{ matrix.package }} -L diff --git a/.github/workflows/security-checks.yml b/.github/workflows/security-checks.yml index 6b7d71e5..6a86f70e 100644 --- a/.github/workflows/security-checks.yml +++ b/.github/workflows/security-checks.yml @@ -24,13 +24,13 @@ jobs: uses: github/codeql-action/upload-sarif@v2 with: sarif_file: ${{github.workspace}}/flawfinder_results.sarif - + codeql: name: CodeQL runs-on: ubuntu-latest container: image: archlinux - + permissions: actions: read contents: read @@ -42,34 +42,25 @@ jobs: language: [ 'cpp' ] steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - - name: Init Hyprland build - run: | - sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff - useradd -m githubuser - echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers - su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar" - git config --global --add safe.directory /__w/Hyprland/Hyprland - - - name: Checkout Hyprland - uses: actions/checkout@v3 + - name: Setup base + uses: ./.github/actions/setup_base with: - submodules: recursive + INSTALL_XORG_PKGS: true - name: Build Hyprland run: | - git submodule sync --recursive && git submodule update --init --force --recursive make all - + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..51f6b91e --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,28 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Mark stale issues and pull requests + +on: + schedule: + - cron: '7 */4 * * *' + workflow_dispatch: + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v5 + with: + repo-token: ${{ secrets.STALEBOT_PAT }} + stale-issue-label: 'stale' + stale-pr-label: 'stale' + operations-per-run: 40 + days-before-close: -1 diff --git a/.gitignore b/.gitignore index dfee530e..b8423e8f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ gmon.out PKGBUILD src/version.h + +.direnv diff --git a/CMakeLists.txt b/CMakeLists.txt index bedb7bbf..857e21de 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,8 +57,8 @@ ExternalProject_Add( wlroots PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots - PATCH_COMMAND sed -E -i -e "s/(soversion = 13)([^032]|$$)/soversion = 13032/g" meson.build - CONFIGURE_COMMAND meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> && meson setup build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> --reconfigure + PATCH_COMMAND sed -E -i -e "s/(soversion = .*$)/soversion = 13032/g" meson.build + CONFIGURE_COMMAND meson setup --reconfigure build --buildtype=${BUILDTYPE_LOWER} -Dwerror=false -Dxwayland=$,disabled,enabled> -Dexamples=false -Drenderers=gles2 $,-Db_sanitize=address,-Db_sanitize=none> BUILD_COMMAND ninja -C build BUILD_ALWAYS true BUILD_IN_SOURCE true @@ -101,7 +101,7 @@ message(STATUS "Checking deps...") find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) find_package(OpenGL REQUIRED) -pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1) # we do not check for wlroots, as we provide it ourselves +pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1 hyprlang>=0.3.2) # we do not check for wlroots, as we provide it ourselves file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") diff --git a/Makefile b/Makefile index 0a0ef8fa..9c5e94d5 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,22 @@ PREFIX = /usr/local legacyrenderer: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build legacyrendererdebug: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build release: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build debug: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -S . -B ./build -G Ninja + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` chmod -R 777 ./build @@ -42,10 +42,10 @@ install: chmod 755 ${PREFIX}/bin/Hyprland chmod 755 ${PREFIX}/bin/hyprctl chmod 755 ${PREFIX}/bin/hyprpm - ln -s -r -f ${PREFIX}/bin/Hyprland ${PREFIX}/bin/hyprland + cd ${PREFIX}/bin && ln -sf Hyprland hyprland if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi mkdir -p ${PREFIX}/share/hyprland - cp ./assets/wall_* ${PREFIX}/share/hyprland + cp ./assets/wall* ${PREFIX}/share/hyprland mkdir -p ${PREFIX}/share/xdg-desktop-portal cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal diff --git a/README.md b/README.md index 82dd0731..9f4aa847 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more... - Much more QoL stuff than other wlr-based compositors - Custom bezier curves for the best animations - Powerful plugin support +- Built-in plugin manager - Tearing support for better gaming performance - Easily expandable and readable codebase - Fast and active development diff --git a/assets/meson.build b/assets/meson.build index cdfafabb..8c4a60ec 100644 --- a/assets/meson.build +++ b/assets/meson.build @@ -1,9 +1,7 @@ -wallpaper_types = ['', 'anime_', 'anime2_'] +wallpapers = ['0', '1', '2'] -foreach type : wallpaper_types - foreach size : [2, 4, 8] - install_data(f'wall_@type@@size@K.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') - endforeach +foreach type : wallpapers + install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') endforeach install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime') diff --git a/assets/wall0.png b/assets/wall0.png new file mode 100644 index 00000000..73bdeef8 Binary files /dev/null and b/assets/wall0.png differ diff --git a/assets/wall1.png b/assets/wall1.png new file mode 100644 index 00000000..53e4c90d Binary files /dev/null and b/assets/wall1.png differ diff --git a/assets/wall2.png b/assets/wall2.png new file mode 100644 index 00000000..9ade4720 Binary files /dev/null and b/assets/wall2.png differ diff --git a/assets/wall_2K.png b/assets/wall_2K.png deleted file mode 100644 index 5aea012a..00000000 Binary files a/assets/wall_2K.png and /dev/null differ diff --git a/assets/wall_4K.png b/assets/wall_4K.png deleted file mode 100644 index f835a978..00000000 Binary files a/assets/wall_4K.png and /dev/null differ diff --git a/assets/wall_8K.png b/assets/wall_8K.png deleted file mode 100644 index 539aa97d..00000000 Binary files a/assets/wall_8K.png and /dev/null differ diff --git a/assets/wall_anime2_2K.png b/assets/wall_anime2_2K.png deleted file mode 100644 index 5a465efa..00000000 Binary files a/assets/wall_anime2_2K.png and /dev/null differ diff --git a/assets/wall_anime2_4K.png b/assets/wall_anime2_4K.png deleted file mode 100644 index b04e6d00..00000000 Binary files a/assets/wall_anime2_4K.png and /dev/null differ diff --git a/assets/wall_anime2_8K.png b/assets/wall_anime2_8K.png deleted file mode 100644 index b8da18ef..00000000 Binary files a/assets/wall_anime2_8K.png and /dev/null differ diff --git a/assets/wall_anime_2K.png b/assets/wall_anime_2K.png deleted file mode 100644 index 202dc493..00000000 Binary files a/assets/wall_anime_2K.png and /dev/null differ diff --git a/assets/wall_anime_4K.png b/assets/wall_anime_4K.png deleted file mode 100644 index 687b79c3..00000000 Binary files a/assets/wall_anime_4K.png and /dev/null differ diff --git a/assets/wall_anime_8K.png b/assets/wall_anime_8K.png deleted file mode 100644 index 25c15cd7..00000000 Binary files a/assets/wall_anime_8K.png and /dev/null differ diff --git a/docs/ISSUE_GUIDELINES.md b/docs/ISSUE_GUIDELINES.md index be011608..c031fe85 100644 --- a/docs/ISSUE_GUIDELINES.md +++ b/docs/ISSUE_GUIDELINES.md @@ -47,7 +47,7 @@ basically, directories in /tmp/hypr are your sessions. ## Obtaining the Hyprland Crash Report (v0.22.0beta and up) -If you have `$XDG_CACHE_HOME` set, the crash report directory is `$XDG_CACHE_HOME/hyprland`. If not, it's `~/.hyprland` +If you have `$XDG_CACHE_HOME` set, the crash report directory is `$XDG_CACHE_HOME/hyprland`. If not, it's `$HOME/.cache/hyprland`. Go to the crash report directory and you should find a file named `hyprlandCrashReport[XXXX].txt` where `[XXXX]` is the PID of the process that crashed. diff --git a/example/hyprland.conf b/example/hyprland.conf index 56d04cae..452b308d 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -5,108 +5,14 @@ # Source a file (multi-file configs) # source = ~/.config/hypr/myColors.conf -# Startup -#exec-once = waybar & hyprpaper & dunst -exec-once = /usr/libexec/polkit-gnome-authentication-agent-1 -exec-once = hyprpm reload -n -#exec-once = wl-clip-persist --clipboard both -#exec-once = wl-paste -p -t text --watch clipman store -P --histpath="~/.local/share/clipman-primary.json" +# Set programs that you use +$terminal = kitty +$fileManager = dolphin +$menu = wofi --show drun -# Plugins -#plugin { -# split-monitor-workspaces { -# count = 10 -# } -# hy3 { -# # disable gaps when only one window is onscreen -# no_gaps_when_only = false # default: false -# -# # policy controlling what happens when a node is removed from a group, -# # leaving only a group -# # 0 = remove the nested group -# # 1 = keep the nested group -# # 2 = keep the nested group only if its parent is a tab group -# node_collapse_policy = 2 # default: 2 -# -# # special workspace -# special_scale_factor = 0.95 -# -# # offset from group split direction when only one window is in a group -# group_inset = 10 # default: 10 -# -# # tab group settings -# tabs { -# # height of the tab bar -# height = 15 # default: 15 -# -# # padding between the tab bar and its focused node -# padding = 5 # default: 5 -# -# # the tab bar should animate in/out from the top instead of below the window -# from_top = false # default: false -# -# # rounding of tab bar corners -# rounding = 5 # default: 3 -# -# # render the window title on the bar -# render_text = true # default: true -# -# # font to render the window title with -# text_font = Jetbrains Mono # default: Sans -# -# # height of the window title -# text_height = 8 # default: 8 -# -# # left padding of the window title -# text_padding = 3 # default: 3 -# -# # active tab bar segment color -# col.active = 0xFF8330DB # default: 0xff32b4ff -# -# # urgent tab bar segment color -# col.urgent = 0xffff4f4f # default: 0xffff4f4f -# -# # inactive tab bar segment color -# col.inactive = 0xFF4D4D4D # default: 0x80808080 -# -# # active tab bar text color -# col.text.active = 0xffffffff # default: 0xff000000 -# -# # urgent tab bar text color -# col.text.urgent = 0xffffffff # default: 0xff000000 -# -# # inactive tab bar text color -# col.text.inactive = 0xffffffff # default: 0xff000000 -# } -# -# # autotiling settings -# autotile { -# # enable autotile -# enable = false # default: false -# -# # make autotile-created groups ephemeral -# ephemeral_groups = true # default: true -# -# # if a window would be squished smaller than this width, a vertical split will be created -# # -1 = never automatically split vertically -# # 0 = always automatically split vertically -# # = pixel height to split at -# trigger_width = 0 # default: 0 -# -# # if a window would be squished smaller than this height, a horizontal split will be created -# # -1 = never automatically split horizontally -# # 0 = always automatically split horizontally -# # = pixel height to split at -# trigger_height = 0 # default: 0 -# -# # a space or comma separated list of workspace ids where autotile should be enabled -# # it's possible to create an exception rule by prefixing the definition with "not:" -# # workspaces = 1,2 # autotiling will only be enabled on workspaces 1 and 2 -# # workspaces = not:1,2 # autotiling will be enabled on all workspaces except 1 and 2 -# workspaces = all # default: all -# } -# } -#} +# Some default env vars. +env = XCURSOR_SIZE,24 +env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that # For all categories, see https://wiki.hyprland.org/Configuring/Variables/ input { @@ -184,33 +90,36 @@ gestures { workspace_swipe = off } -# unscale XWayland -xwayland { - force_zero_scaling = true +misc { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers } +# Example per-device config +# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more +device { + name = epic-mouse-v1 + sensitivity = -0.5 +} + +# Example windowrule v1 +# windowrule = float, ^(kitty)$ +# Example windowrule v2 +# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # See https://wiki.hyprland.org/Configuring/Window-Rules/ for more -windowrule = rounding 1,class:^(Wine)$ -windowrulev2 = opacity 0.8 0.8,class:^(kitty)$ -windowrulev2=float,class:^(hyprland.share.picker)$ -windowrule = float,^(Steamwebhelper)$ -windowrule = float, title:^(Steamwebhelper)$ -windowrule = float, title:^(pop-up) -windowrule = float, class:^(xdg-desktop-portal-kde) -windowrule = float, class:^(Wine) +windowrulev2 = suppressevent maximize, class:.* # You'll probably like this. + + # See https://wiki.hyprland.org/Configuring/Keywords/ for more $mainMod = SUPER # Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more -bind = $mainMod, RETURN, exec, $terminal -bind = $mainMod, Q, killactive, -bind = $mainMod SHIFT, Q, exit, -bind = $mainMod, F, fullscreen, -bind = $mainMod, SPACE, togglefloating, -bind = $mainMod, L, exec, gtklock -bind = $mainMod CTRL, B, exec, killall waybar && waybar -c /home/$USER/.config/waybar/config-hypr - -bind = $mainMod, D, exec, rofi -show run +bind = $mainMod, Q, exec, $terminal +bind = $mainMod, C, killactive, +bind = $mainMod, M, exit, +bind = $mainMod, E, exec, $fileManager +bind = $mainMod, V, togglefloating, +bind = $mainMod, R, exec, $menu bind = $mainMod, P, pseudo, # dwindle bind = $mainMod, J, togglesplit, # dwindle bind = $mainMod, Print, exec, grim -g "$(slurp)" - | swappy -f - # take a screenshot diff --git a/flake.lock b/flake.lock index f7761728..d5e00c3b 100644 --- a/flake.lock +++ b/flake.lock @@ -23,13 +23,36 @@ "type": "github" } }, + "hyprlang": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1708787654, + "narHash": "sha256-7ACgM3ZuAhPqurXHUvR2nWMRcnmzGGPjLK6q4DSTelI=", + "owner": "hyprwm", + "repo": "hyprlang", + "rev": "0fce791ba2334aca183f2ed42399518947550d0d", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprlang", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1703438236, - "narHash": "sha256-aqVBq1u09yFhL7bj1/xyUeJjzr92fXVvQSSEx6AdB1M=", + "lastModified": 1708807242, + "narHash": "sha256-sRTRkhMD4delO/hPxxi+XwLqPn8BuUq6nnj4JqLwOu0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5f64a12a728902226210bf01d25ec6cbb9d9265b", + "rev": "73de017ef2d18a04ac4bfd0c02650007ccb31c2a", "type": "github" }, "original": { @@ -42,6 +65,7 @@ "root": { "inputs": { "hyprland-protocols": "hyprland-protocols", + "hyprlang": "hyprlang", "nixpkgs": "nixpkgs", "systems": "systems", "wlroots": "wlroots", @@ -67,18 +91,18 @@ "flake": false, "locked": { "host": "gitlab.freedesktop.org", - "lastModified": 1701368958, - "narHash": "sha256-7kvyoA91etzVEl9mkA/EJfB6z/PltxX7Xc4gcr7/xlo=", + "lastModified": 1708558866, + "narHash": "sha256-Mz6hCtommq7RQfcPnxLINigO4RYSNt23HeJHC6mVmWI=", "owner": "wlroots", "repo": "wlroots", - "rev": "5d639394f3e83b01596dcd166a44a9a1a2583350", + "rev": "0cb091f1a2d345f37d2ee445f4ffd04f7f4ec9e5", "type": "gitlab" }, "original": { "host": "gitlab.freedesktop.org", "owner": "wlroots", "repo": "wlroots", - "rev": "5d639394f3e83b01596dcd166a44a9a1a2583350", + "rev": "0cb091f1a2d345f37d2ee445f4ffd04f7f4ec9e5", "type": "gitlab" } }, @@ -87,6 +111,9 @@ "hyprland-protocols": [ "hyprland-protocols" ], + "hyprlang": [ + "hyprlang" + ], "nixpkgs": [ "nixpkgs" ], @@ -95,11 +122,11 @@ ] }, "locked": { - "lastModified": 1703514399, - "narHash": "sha256-VRr5Xc4S/VPr/gU3fiOD3vSIL2+GJ+LUrmFTWTwnTz4=", + "lastModified": 1708696469, + "narHash": "sha256-shh5wmpeYy3MmsBfkm4f76yPsBDGk6OLYRVG+ARy2F0=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "0a318a7a217a6402b0b705837cd5b50b0e94b31b", + "rev": "1b713911c2f12b96c2574474686e4027ac4bf826", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6807c0f3..675a6d92 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ host = "gitlab.freedesktop.org"; owner = "wlroots"; repo = "wlroots"; - rev = "5d639394f3e83b01596dcd166a44a9a1a2583350"; + rev = "0cb091f1a2d345f37d2ee445f4ffd04f7f4ec9e5"; flake = false; }; @@ -22,11 +22,18 @@ inputs.systems.follows = "systems"; }; + hyprlang = { + url = "github:hyprwm/hyprlang"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.systems.follows = "systems"; + }; + xdph = { url = "github:hyprwm/xdg-desktop-portal-hyprland"; inputs.nixpkgs.follows = "nixpkgs"; inputs.systems.follows = "systems"; inputs.hyprland-protocols.follows = "hyprland-protocols"; + inputs.hyprlang.follows = "hyprlang"; }; }; @@ -86,6 +93,7 @@ name = "hyprland-shell"; nativeBuildInputs = with pkgsFor.${system}; [cmake python3]; buildInputs = [self.packages.${system}.wlroots-hyprland]; + hardeningDisable = ["fortify"]; inputsFrom = [ self.packages.${system}.wlroots-hyprland self.packages.${system}.hyprland diff --git a/hyprctl/main.cpp b/hyprctl/main.cpp index f11cac6e..11b571ac 100644 --- a/hyprctl/main.cpp +++ b/hyprctl/main.cpp @@ -86,7 +86,7 @@ std::vector instances() { std::vector result; for (const auto& el : std::filesystem::directory_iterator("/tmp/hypr")) { - if (el.is_directory()) + if (el.is_directory() || !el.path().string().ends_with(".lock")) continue; // read lock @@ -94,7 +94,9 @@ std::vector instances() { data->id = el.path().string(); data->id = data->id.substr(data->id.find_last_of('/') + 1, data->id.find(".lock") - data->id.find_last_of('/') - 1); - data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1)); + try { + data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1)); + } catch (std::exception& e) { continue; } // read file std::ifstream ifs(el.path().string()); @@ -102,7 +104,9 @@ std::vector instances() { int i = 0; for (std::string line; std::getline(ifs, line); ++i) { if (i == 0) { - data->pid = std::stoull(line); + try { + data->pid = std::stoull(line); + } catch (std::exception& e) { continue; } } else if (i == 1) { data->wlSocket = line; } else @@ -309,6 +313,8 @@ int main(int argc, char** argv) { if (ARGS[i] == "-j" && !fullArgs.contains("j")) { fullArgs += "j"; json = true; + } else if (ARGS[i] == "-r" && !fullArgs.contains("r")) { + fullArgs += "r"; } else if (ARGS[i] == "--batch") { fullRequest = "--batch "; } else if (ARGS[i] == "--instance" || ARGS[i] == "-i") { @@ -368,7 +374,7 @@ int main(int argc, char** argv) { const auto ISIG = getenv("HYPRLAND_INSTANCE_SIGNATURE"); if (!ISIG) { - std::cout << "HYPRLAND_INSTANCE_SIGNATURE not set! (is hyprland running?)"; + std::cout << "HYPRLAND_INSTANCE_SIGNATURE not set! (is hyprland running?)\n"; return 1; } @@ -442,8 +448,7 @@ int main(int argc, char** argv) { else if (fullRequest.contains("/--help")) printf("%s", USAGE.c_str()); else { - printf("%s\n", USAGE.c_str()); - return 1; + request(fullRequest); } printf("\n"); diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp index d95f10bb..b4fc8dd5 100644 --- a/hyprpm/src/core/DataState.cpp +++ b/hyprpm/src/core/DataState.cpp @@ -20,11 +20,18 @@ std::string DataState::getDataStatePath() { return std::string{HOME} + "/.local/share/hyprpm"; } +std::string DataState::getHeadersPath() { + return getDataStatePath() + "/headersRoot"; +} + void DataState::ensureStateStoreExists() { const auto PATH = getDataStatePath(); if (!std::filesystem::exists(PATH)) std::filesystem::create_directories(PATH); + + if (!std::filesystem::exists(getHeadersPath())) + std::filesystem::create_directories(getHeadersPath()); } void DataState::addNewPluginRepo(const SPluginRepository& repo) { @@ -47,7 +54,8 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { DATA.emplace(p.name, toml::table{ {"filename", p.name + ".so"}, - {"enabled", p.enabled} + {"enabled", p.enabled}, + {"failed", p.failed} }); } // clang-format on @@ -63,7 +71,10 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -84,7 +95,10 @@ void DataState::removePluginRepo(const std::string& urlOrName) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -153,7 +167,10 @@ std::vector DataState::getAllRepositories() { std::vector repos; for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -172,9 +189,10 @@ std::vector DataState::getAllRepositories() { continue; const auto ENABLED = STATE[key]["enabled"].value_or(false); + const auto FAILED = STATE[key]["failed"].value_or(false); const auto FILENAME = STATE[key]["filename"].value_or(""); - repo.plugins.push_back(SPlugin{std::string{key.str()}, FILENAME, ENABLED}); + repo.plugins.push_back(SPlugin{std::string{key.str()}, FILENAME, ENABLED, FAILED}); } repos.push_back(repo); @@ -189,7 +207,10 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { const auto PATH = getDataStatePath(); for (const auto& entry : std::filesystem::directory_iterator(PATH)) { - if (!entry.is_directory()) + if (!entry.is_directory() || entry.path().stem() == "headersRoot") + continue; + + if (!std::filesystem::exists(entry.path().string() + "/state.toml")) continue; auto STATE = toml::parse_file(entry.path().string() + "/state.toml"); @@ -201,6 +222,11 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { if (key.str() != name) continue; + const auto FAILED = STATE[key]["failed"].value_or(false); + + if (FAILED) + return false; + (*STATE[key].as_table()).insert_or_assign("enabled", enabled); std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc); @@ -212,4 +238,4 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) { } return false; -} \ No newline at end of file +} diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp index ac81dae1..c4a7c12d 100644 --- a/hyprpm/src/core/DataState.hpp +++ b/hyprpm/src/core/DataState.hpp @@ -10,6 +10,7 @@ struct SGlobalState { namespace DataState { std::string getDataStatePath(); + std::string getHeadersPath(); void ensureStateStoreExists(); void addNewPluginRepo(const SPluginRepository& repo); void removePluginRepo(const std::string& urlOrName); @@ -18,4 +19,4 @@ namespace DataState { SGlobalState getGlobalState(); bool setPluginEnabled(const std::string& name, bool enabled); std::vector getAllRepositories(); -}; \ No newline at end of file +}; diff --git a/hyprpm/src/core/Manifest.hpp b/hyprpm/src/core/Manifest.hpp index 63e1791f..10b326c0 100644 --- a/hyprpm/src/core/Manifest.hpp +++ b/hyprpm/src/core/Manifest.hpp @@ -19,6 +19,7 @@ class CManifest { std::vector authors; std::vector buildSteps; std::string output; + bool failed = false; }; struct { @@ -29,4 +30,4 @@ class CManifest { std::vector m_vPlugins; bool m_bGood = true; -}; \ No newline at end of file +}; diff --git a/hyprpm/src/core/Plugin.hpp b/hyprpm/src/core/Plugin.hpp index 32c02a49..edc7486a 100644 --- a/hyprpm/src/core/Plugin.hpp +++ b/hyprpm/src/core/Plugin.hpp @@ -6,7 +6,8 @@ struct SPlugin { std::string name; std::string filename; - bool enabled; + bool enabled = false; + bool failed = false; }; struct SPluginRepository { @@ -14,4 +15,4 @@ struct SPluginRepository { std::string name; std::vector plugins; std::string hash; -}; \ No newline at end of file +}; diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index e5904d66..4b026090 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -78,6 +79,8 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() { bool CPluginManager::addNewPluginRepo(const std::string& url) { + const auto HLVER = getHyprlandVersion(); + if (DataState::pluginRepoExists(url)) { std::cerr << "\n" << Colors::RED << "✖" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n"; return false; @@ -170,6 +173,22 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) { message += " version " + pl.version; progress.printMessageAbove(message); } + + if (!pManifest->m_sRepository.commitPins.empty()) { + // check commit pins + + progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking"); + + for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) { + if (hl != HLVER.hash) + continue; + + progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting"); + + execAndGet("cd /tmp/hyprpm/new/ && git reset --hard --recurse-submodules " + plugin); + } + } + progress.m_szCurrentMessage = "Verifying headers"; progress.print(); @@ -191,16 +210,19 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) { progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); for (auto& bs : p.buildSteps) { - out += execAndGet("cd /tmp/hyprpm/new && " + bs) + "\n"; + std::string cmd = std::format("cd /tmp/hyprpm/new && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) { - std::cerr << "\n" << Colors::RED << "✖" << Colors::RESET << " Plugin " << p.name << " failed to build.\n"; + progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " Plugin " + p.name + " failed to build.\n"); if (m_bVerbose) std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n"; - return false; + p.failed = true; + + continue; } progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " built " + p.name + " into " + p.output); @@ -220,7 +242,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url) { repo.url = url; repo.hash = repohash; for (auto& p : pManifest->m_vPlugins) { - repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false}); + repo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/new/" + p.output, false, p.failed}); } DataState::addNewPluginRepo(repo); @@ -263,8 +285,12 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) { eHeadersErrors CPluginManager::headersValid() { const auto HLVER = getHyprlandVersion(); + if (!std::filesystem::exists(DataState::getHeadersPath() + "/share/pkgconfig/hyprland.pc")) + return HEADERS_MISSING; + // find headers commit - auto headers = execAndGet("pkg-config --cflags --keep-system-cflags hyprland"); + std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkg-config --cflags --keep-system-cflags hyprland", DataState::getHeadersPath()); + auto headers = execAndGet(cmd.c_str()); if (!headers.contains("-I/")) return HEADERS_MISSING; @@ -296,10 +322,6 @@ eHeadersErrors CPluginManager::headersValid() { if (!ifs.good()) return HEADERS_CORRUPTED; - if ((std::filesystem::exists("/usr/include/hyprland/src/version.h") && verHeader != "/usr/include/hyprland/src/version.h") || - (std::filesystem::exists("/usr/local/include/hyprland/src/version.h") && verHeader != "/usr/local/include/hyprland/src/version.h")) - return HEADERS_DUPLICATED; - std::string verHeaderContent((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); ifs.close(); @@ -314,7 +336,9 @@ eHeadersErrors CPluginManager::headersValid() { return HEADERS_OK; } -bool CPluginManager::updateHeaders() { +bool CPluginManager::updateHeaders(bool force) { + + DataState::ensureStateStoreExists(); const auto HLVER = getHyprlandVersion(); @@ -323,11 +347,8 @@ bool CPluginManager::updateHeaders() { std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace); } - if (headersValid() == HEADERS_OK) { - std::cout << "\n" << std::string{Colors::GREEN} + "✔" + Colors::RESET + " Your headers are already up-to-date.\n"; - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersHashCompiled = HLVER.hash; - DataState::updateGlobalState(GLOBALSTATE); + if (!force && headersValid() == HEADERS_OK) { + std::cout << "\n" << std::string{Colors::GREEN} + "✔" + Colors::RESET + " Headers up to date.\n"; return true; } @@ -342,9 +363,9 @@ bool CPluginManager::updateHeaders() { std::filesystem::remove_all("/tmp/hyprpm/hyprland"); } - progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/dragontos/hyprland, this might take a moment."); + progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment."); - std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/dragontos/hyprland hyprland"); + std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland"); if (!std::filesystem::exists("/tmp/hyprpm/hyprland")) { std::cerr << "\n" << Colors::RED << "✖" << Colors::RESET << " Could not clone the hyprland repository. shell returned:\n" << ret << "\n"; @@ -369,23 +390,38 @@ bool CPluginManager::updateHeaders() { progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland"); - ret = execAndGet("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja"); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath()); + + ret = execAndGet( + std::format("cd /tmp/hyprpm/hyprland && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", + DataState::getHeadersPath())); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret); + // le hack. Wlroots has to generate its build/include ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build"); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "meson returned: " + ret); progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " configured Hyprland"); progress.m_iSteps = 4; progress.m_szCurrentMessage = "Installing sources"; progress.print(); - progress.printMessageAbove( - std::string{Colors::YELLOW} + "!" + Colors::RESET + - " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running."); + // progress.printMessageAbove( + // std::string{Colors::YELLOW} + "!" + Colors::RESET + + // " in order to install the sources, you will need to input your password.\n If nothing pops up, make sure you have polkit and an authentication daemon running."); - ret = execAndGet("pkexec sh \"-c\" \"cd /tmp/hyprpm/hyprland && make installheaders\""); + std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" /tmp/hyprpm/hyprland/Makefile && cd /tmp/hyprpm/hyprland && make installheaders", + DataState::getHeadersPath()); + if (m_bVerbose) + progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd); + + ret = execAndGet(cmd); if (m_bVerbose) - std::cout << Colors::BLUE << "[v] " << Colors::RESET << "pkexec returned: " << ret << "\n"; + std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n"; // remove build files std::filesystem::remove_all("/tmp/hyprpm/hyprland"); @@ -397,10 +433,6 @@ bool CPluginManager::updateHeaders() { progress.m_szCurrentMessage = "Done!"; progress.print(); - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersHashCompiled = HLVER.hash; - DataState::updateGlobalState(GLOBALSTATE); - std::cout << "\n"; } else { progress.printMessageAbove(std::string{Colors::RED} + "✖" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID)); @@ -434,7 +466,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { const auto HLVER = getHyprlandVersion(); CProgressBar progress; - progress.m_iMaxSteps = REPOS.size() * 2 + 1; + progress.m_iMaxSteps = REPOS.size() * 2 + 2; progress.m_iSteps = 0; progress.m_szCurrentMessage = "Updating repositories"; progress.print(); @@ -528,7 +560,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); for (auto& bs : p.buildSteps) { - out += execAndGet("cd /tmp/hyprpm/update && " + bs) + "\n"; + std::string cmd = std::format("cd /tmp/hyprpm/update && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) { @@ -566,6 +599,14 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(std::string{Colors::GREEN} + "✔" + Colors::RESET + " updated " + repo.name); } + progress.m_iSteps++; + progress.m_szCurrentMessage = "Updating global state..."; + progress.print(); + + auto GLOBALSTATE = DataState::getGlobalState(); + GLOBALSTATE.headersHashCompiled = HLVER.hash; + DataState::updateGlobalState(GLOBALSTATE); + progress.m_iSteps++; progress.m_szCurrentMessage = "Done!"; progress.print(); @@ -696,8 +737,13 @@ void CPluginManager::listAllPlugins() { std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n"; for (auto& p : r.plugins) { - std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name + "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") - << Colors::RESET << "\n"; + + std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name; + + if (!p.failed) + std::cout << "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") << Colors::RESET << "\n"; + else + std::cout << "\n └─ enabled: " << Colors::RED << "Plugin failed to build" << Colors::RESET << "\n"; } } } diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 3c5c7c5c..5e1eb6fb 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -40,7 +40,7 @@ class CPluginManager { bool removePluginRepo(const std::string& urlOrName); eHeadersErrors headersValid(); - bool updateHeaders(); + bool updateHeaders(bool force = false); bool updatePlugins(bool forceUpdateAll); void listAllPlugins(); @@ -60,4 +60,4 @@ class CPluginManager { std::string headerError(const eHeadersErrors err); }; -inline std::unique_ptr g_pPluginManager; \ No newline at end of file +inline std::unique_ptr g_pPluginManager; diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp index 2acc3d39..c0697e29 100644 --- a/hyprpm/src/main.cpp +++ b/hyprpm/src/main.cpp @@ -1,6 +1,7 @@ #include "progress/CProgressBar.hpp" #include "helpers/Colors.hpp" #include "core/PluginManager.hpp" +#include "core/DataState.hpp" #include #include @@ -37,7 +38,7 @@ int main(int argc, char** argv, char** envp) { } std::vector command; - bool notify = false, verbose = false; + bool notify = false, verbose = false, force = false; for (int i = 1; i < argc; ++i) { if (ARGS[i].starts_with("-")) { @@ -48,6 +49,9 @@ int main(int argc, char** argv, char** envp) { notify = true; } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { verbose = true; + } else if (ARGS[i] == "--force" || ARGS[i] == "-f") { + force = true; + std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n"; } else { std::cerr << "Unrecognized option " << ARGS[i]; return 1; @@ -81,9 +85,13 @@ int main(int argc, char** argv, char** envp) { return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; } else if (command[0] == "update") { bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; - bool headers = g_pPluginManager->updateHeaders(); + bool headers = g_pPluginManager->updateHeaders(force); if (headers) { - bool ret1 = g_pPluginManager->updatePlugins(!headersValid); + const auto HLVER = g_pPluginManager->getHyprlandVersion(); + auto GLOBALSTATE = DataState::getGlobalState(); + const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled; + + bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED); if (!ret1) return 1; diff --git a/nix/default.nix b/nix/default.nix index 751457ca..6062a215 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -9,6 +9,7 @@ cairo, git, hyprland-protocols, + hyprlang, jq, libGL, libdrm, @@ -75,6 +76,7 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov cairo git hyprland-protocols + hyprlang libdrm libGL libinput diff --git a/nix/overlays.nix b/nix/overlays.nix index 5eb7b082..0a84d93e 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -10,21 +10,19 @@ (builtins.substring 4 2 longDate) (builtins.substring 6 2 longDate) ]); - - mkJoinedOverlays = overlays: final: prev: - lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays; in { # Contains what a user is most likely to care about: # Hyprland itself, XDPH and the Share Picker. - default = mkJoinedOverlays (with self.overlays; [ + default = lib.composeManyExtensions (with self.overlays; [ hyprland-packages hyprland-extras ]); # Packages for variations of Hyprland, dependencies included. - hyprland-packages = mkJoinedOverlays [ + hyprland-packages = lib.composeManyExtensions [ # Dependencies inputs.hyprland-protocols.overlays.default + inputs.hyprlang.overlays.default self.overlays.wlroots-hyprland self.overlays.udis86 # Hyprland packages themselves @@ -34,9 +32,9 @@ in { hyprland = final.callPackage ./default.nix { stdenv = final.gcc13Stdenv; version = "${props.version}+date=${date}_${self.shortRev or "dirty"}"; - wlroots = final.wlroots-hyprland; commit = self.rev or ""; - inherit (final) udis86 hyprland-protocols; + wlroots = final.wlroots-hyprland; # explicit override until decided on breaking change of the name + udis86 = final.udis86-hyprland; # explicit override until decided on breaking change of the name inherit date; }; hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; @@ -59,12 +57,12 @@ in { # Packages for extra software recommended for usage with Hyprland, # including forked or patched packages for compatibility. - hyprland-extras = mkJoinedOverlays [ + hyprland-extras = lib.composeManyExtensions [ inputs.xdph.overlays.xdg-desktop-portal-hyprland ]; udis86 = final: prev: { - udis86 = final.callPackage ./udis86.nix {}; + udis86-hyprland = final.callPackage ./udis86.nix {}; }; # Patched version of wlroots for Hyprland. diff --git a/nix/patches/meson-build.patch b/nix/patches/meson-build.patch index 844eacae..aefbf1bc 100644 --- a/nix/patches/meson-build.patch +++ b/nix/patches/meson-build.patch @@ -34,16 +34,17 @@ index 1d2c7f9f..c5ef4e67 100644 headers = globber.stdout().strip().split('\n') foreach file : headers diff --git a/src/meson.build b/src/meson.build -index 0af864b9..38723b8c 100644 +index 45701f5f..3505cefe 100644 --- a/src/meson.build +++ b/src/meson.build -@@ -9,16 +9,16 @@ executable('Hyprland', src, +@@ -9,17 +9,17 @@ executable('Hyprland', src, server_protos, dependency('wayland-server'), dependency('wayland-client'), - wlroots.get_variable('wlroots'), + dependency('wlroots'), dependency('cairo'), + dependency('hyprlang', version: '>= 0.3.2'), dependency('libdrm'), dependency('egl'), dependency('xkbcommon'), diff --git a/nix/update-wlroots.sh b/nix/update-wlroots.sh index addf2df2..01f5cd83 100755 --- a/nix/update-wlroots.sh +++ b/nix/update-wlroots.sh @@ -8,7 +8,7 @@ CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }') if [ "$SUB_REV" != "$CRT_REV" ]; then echo "Updating wlroots..." # update wlroots to submodule revision - sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap + sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix nix flake lock echo "wlroots: $CRT_REV -> $SUB_REV" diff --git a/props.json b/props.json index 93c30160..557042f5 100644 --- a/props.json +++ b/props.json @@ -1,3 +1,3 @@ { - "version": "0.34.0" -} \ No newline at end of file + "version": "0.36.0" +} diff --git a/protocols/meson.build b/protocols/meson.build index cc111e98..857df50f 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.25', + version: '>=1.32', fallback: 'wayland-protocols', default_options: ['tests=false'], ) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 5ac6b303..f0d7ca0f 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -81,26 +81,6 @@ CCompositor::CCompositor() { CCompositor::~CCompositor() { cleanup(); - g_pDecorationPositioner.reset(); - g_pPluginSystem.reset(); - g_pHyprNotificationOverlay.reset(); - g_pDebugOverlay.reset(); - g_pEventManager.reset(); - g_pSessionLockManager.reset(); - g_pProtocolManager.reset(); - g_pXWaylandManager.reset(); - g_pHyprRenderer.reset(); - g_pHyprOpenGL.reset(); - g_pInputManager.reset(); - g_pThreadManager.reset(); - g_pConfigManager.reset(); - g_pLayoutManager.reset(); - g_pHyprError.reset(); - g_pConfigManager.reset(); - g_pAnimationManager.reset(); - g_pKeybindManager.reset(); - g_pHookSystem.reset(); - g_pWatchdog.reset(); } void CCompositor::setRandomSplash() { @@ -119,8 +99,11 @@ void CCompositor::initServer() { // register crit signal handler wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr); - signal(SIGSEGV, handleUnrecoverableSignal); - signal(SIGABRT, handleUnrecoverableSignal); + + if (!envEnabled("HYPRLAND_NO_CRASHREPORTER")) { + signal(SIGSEGV, handleUnrecoverableSignal); + signal(SIGABRT, handleUnrecoverableSignal); + } signal(SIGUSR1, handleUserSignal); initManagers(STAGE_PRIORITY); @@ -135,10 +118,10 @@ void CCompositor::initServer() { else wlr_log_init(WLR_ERROR, Debug::wlrLog); - m_sWLRBackend = wlr_backend_autocreate(m_sWLDisplay, &m_sWLRSession); + m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession); if (!m_sWLRBackend) { - Debug::log(CRIT, "m_sWLRBackend was NULL!"); + Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues."); throwError("wlr_backend_autocreate() failed!"); } @@ -151,7 +134,7 @@ void CCompositor::initServer() { m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD); if (!m_sWLRRenderer) { - Debug::log(CRIT, "m_sWLRRenderer was NULL!"); + Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues."); throwError("wlr_gles2_renderer_create_with_drm_fd() failed!"); } @@ -259,7 +242,7 @@ void CCompositor::initServer() { m_sWLRActivation = wlr_xdg_activation_v1_create(m_sWLDisplay); - m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLDisplay); + m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop); m_sWLRSessionLockMgr = wlr_session_lock_manager_v1_create(m_sWLDisplay); @@ -324,6 +307,7 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRGammaCtrlMgr->events.set_gamma, &Events::listen_setGamma, m_sWLRGammaCtrlMgr, "GammaCtrlMgr"); addWLSignal(&m_sWLRCursorShapeMgr->events.request_set_shape, &Events::listen_setCursorShape, m_sWLRCursorShapeMgr, "CursorShapeMgr"); addWLSignal(&m_sWLRTearingControlMgr->events.new_object, &Events::listen_newTearingHint, m_sWLRTearingControlMgr, "TearingControlMgr"); + addWLSignal(&m_sWLRKbShInhibitMgr->events.new_inhibitor, &Events::listen_newShortcutInhibitor, m_sWLRKbShInhibitMgr, "ShortcutInhibitMgr"); if (m_sWRLDRMLeaseMgr) addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM"); @@ -332,10 +316,67 @@ void CCompositor::initAllSignals() { addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session"); } +void CCompositor::removeAllSignals() { + removeWLSignal(&Events::listen_newOutput); + removeWLSignal(&Events::listen_newXDGToplevel); + removeWLSignal(&Events::listen_mouseMove); + removeWLSignal(&Events::listen_mouseMoveAbsolute); + removeWLSignal(&Events::listen_mouseButton); + removeWLSignal(&Events::listen_mouseAxis); + removeWLSignal(&Events::listen_mouseFrame); + removeWLSignal(&Events::listen_swipeBegin); + removeWLSignal(&Events::listen_swipeUpdate); + removeWLSignal(&Events::listen_swipeEnd); + removeWLSignal(&Events::listen_pinchBegin); + removeWLSignal(&Events::listen_pinchUpdate); + removeWLSignal(&Events::listen_pinchEnd); + removeWLSignal(&Events::listen_touchBegin); + removeWLSignal(&Events::listen_touchEnd); + removeWLSignal(&Events::listen_touchUpdate); + removeWLSignal(&Events::listen_touchFrame); + removeWLSignal(&Events::listen_holdBegin); + removeWLSignal(&Events::listen_holdEnd); + removeWLSignal(&Events::listen_newInput); + removeWLSignal(&Events::listen_requestMouse); + removeWLSignal(&Events::listen_requestSetSel); + removeWLSignal(&Events::listen_requestDrag); + removeWLSignal(&Events::listen_startDrag); + removeWLSignal(&Events::listen_requestSetSel); + removeWLSignal(&Events::listen_requestSetPrimarySel); + removeWLSignal(&Events::listen_newLayerSurface); + removeWLSignal(&Events::listen_change); + removeWLSignal(&Events::listen_outputMgrApply); + removeWLSignal(&Events::listen_outputMgrTest); + removeWLSignal(&Events::listen_newConstraint); + removeWLSignal(&Events::listen_NewXDGDeco); + removeWLSignal(&Events::listen_newVirtPtr); + removeWLSignal(&Events::listen_newVirtualKeyboard); + removeWLSignal(&Events::listen_RendererDestroy); + removeWLSignal(&Events::listen_newIdleInhibitor); + removeWLSignal(&Events::listen_powerMgrSetMode); + removeWLSignal(&Events::listen_newIME); + removeWLSignal(&Events::listen_newTextInput); + removeWLSignal(&Events::listen_activateXDG); + removeWLSignal(&Events::listen_newSessionLock); + removeWLSignal(&Events::listen_setGamma); + removeWLSignal(&Events::listen_setCursorShape); + removeWLSignal(&Events::listen_newTearingHint); + removeWLSignal(&Events::listen_newShortcutInhibitor); + + if (m_sWRLDRMLeaseMgr) + removeWLSignal(&Events::listen_leaseRequest); + + if (m_sWLRSession) + removeWLSignal(&Events::listen_sessionActive); +} + void CCompositor::cleanup() { if (!m_sWLDisplay || m_bIsShuttingDown) return; + signal(SIGABRT, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + removeLockFile(); m_bIsShuttingDown = true; @@ -362,8 +403,8 @@ void CCompositor::cleanup() { for (auto& m : m_vMonitors) { g_pHyprOpenGL->destroyMonitorResources(m.get()); - wlr_output_enable(m->output, false); - wlr_output_commit(m->output); + wlr_output_state_set_enabled(m->state.wlr(), false); + m->state.commit(); } m_vMonitors.clear(); @@ -373,8 +414,32 @@ void CCompositor::cleanup() { g_pXWaylandManager->m_sWLRXWayland = nullptr; } + removeAllSignals(); + + g_pInputManager.reset(); + wl_display_destroy_clients(g_pCompositor->m_sWLDisplay); + g_pDecorationPositioner.reset(); + g_pPluginSystem.reset(); + g_pHyprNotificationOverlay.reset(); + g_pDebugOverlay.reset(); + g_pEventManager.reset(); + g_pSessionLockManager.reset(); + g_pProtocolManager.reset(); + g_pHyprRenderer.reset(); + g_pHyprOpenGL.reset(); + g_pThreadManager.reset(); + g_pConfigManager.reset(); + g_pLayoutManager.reset(); + g_pHyprError.reset(); + g_pConfigManager.reset(); + g_pAnimationManager.reset(); + g_pKeybindManager.reset(); + g_pHookSystem.reset(); + g_pWatchdog.reset(); + g_pXWaylandManager.reset(); + wl_display_terminate(m_sWLDisplay); m_sWLDisplay = nullptr; @@ -408,6 +473,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the ThreadManager!"); g_pThreadManager = std::make_unique(); + Debug::log(LOG, "Creating CHyprCtl"); + g_pHyprCtl = std::make_unique(); + Debug::log(LOG, "Creating the InputManager!"); g_pInputManager = std::make_unique(); @@ -629,72 +697,32 @@ bool CCompositor::windowExists(CWindow* pWindow) { return false; } -CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) { - const auto PMONITOR = getMonitorFromVector(pos); - - if (PMONITOR->specialWorkspaceID) { - for (auto& w : m_vWindows) { - CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y}; - if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && box.containsPoint(pos) && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus) - return w.get(); - } +bool CCompositor::monitorExists(CMonitor* pMonitor) { + for (auto& m : m_vRealMonitors) { + if (m.get() == pMonitor) + return true; } - for (auto& w : m_vWindows) { - CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y}; - if (w->m_bIsMapped && box.containsPoint(pos) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus) - return w.get(); - } - - return nullptr; + return false; } -CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreWindow) { +CWindow* CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, CWindow* pIgnoreWindow) { const auto PMONITOR = getMonitorFromVector(pos); - static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue; - static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; - static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue; - const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0; + static auto* const PRESIZEONBORDER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:resize_on_border"); + static auto* const PBORDERSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:border_size"); + static auto* const PBORDERGRABEXTEND = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area"); + static auto* const PSPECIALFALLTHRU = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:special_fallthrough"); + const auto BORDER_GRAB_AREA = **PRESIZEONBORDER ? **PBORDERSIZE + **PBORDERGRABEXTEND : 0; // pinned windows on top of floating regardless - for (auto& w : m_vWindows | std::views::reverse) { - const auto BB = w->getWindowInputBox(); - CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; - if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_bNoFocus && w.get() != pIgnoreWindow) { - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) - return w.get(); - - if (!w->m_bIsX11) { - if (w->hasPopupAt(pos)) - return w.get(); - } - } - } - - auto windowForWorkspace = [&](bool special) -> CWindow* { - // first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter. + if (properties & ALLOW_FLOATING) { for (auto& w : m_vWindows | std::views::reverse) { - - if (special && !isWorkspaceSpecial(w->m_iWorkspaceID)) // because special floating may creep up into regular - continue; - - const auto BB = w->getWindowInputBox(); + const auto BB = w->getWindowBoxUnified(properties); CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; - if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned && !w->m_bNoFocus && w.get() != pIgnoreWindow) { - // OR windows should add focus to parent - if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2) - continue; - - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) { - - if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) { - // Override Redirect - return g_pCompositor->m_pLastWindow; // we kinda trick everything here. - // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases. - } - + if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_sAdditionalConfigData.noFocus && + w.get() != pIgnoreWindow) { + if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) return w.get(); - } if (!w->m_bIsX11) { if (w->hasPopupAt(pos)) @@ -702,6 +730,53 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW } } } + } + + auto windowForWorkspace = [&](bool special) -> CWindow* { + auto floating = [&](bool aboveFullscreen) -> CWindow* { + for (auto& w : m_vWindows | std::views::reverse) { + + if (special && !isWorkspaceSpecial(w->m_iWorkspaceID)) // because special floating may creep up into regular + continue; + + const auto BB = w->getWindowBoxUnified(properties); + CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; + if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned && !w->m_sAdditionalConfigData.noFocus && + w.get() != pIgnoreWindow && (!aboveFullscreen || w->m_bCreatedOverFullscreen)) { + // OR windows should add focus to parent + if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2) + continue; + + if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y})) { + + if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) { + // Override Redirect + return g_pCompositor->m_pLastWindow; // we kinda trick everything here. + // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases. + } + + return w.get(); + } + + if (!w->m_bIsX11) { + if (w->hasPopupAt(pos)) + return w.get(); + } + } + } + + return nullptr; + }; + + if (properties & ALLOW_FLOATING) { + // first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter. + auto found = floating(true); + if (found) + return found; + } + + if (properties & FLOATING_ONLY) + return floating(false); const int64_t WORKSPACEID = special ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace; const auto PWORKSPACE = getWorkspaceByID(WORKSPACEID); @@ -709,13 +784,17 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW if (PWORKSPACE->m_bHasFullscreenWindow) return getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); + auto found = floating(false); + if (found) + return found; + // for windows, we need to check their extensions too, first. for (auto& w : m_vWindows) { if (special != isWorkspaceSpecial(w->m_iWorkspaceID)) continue; - if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == WORKSPACEID && !w->isHidden() && !w->m_bX11ShouldntFocus && !w->m_bNoFocus && - w.get() != pIgnoreWindow) { + if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == WORKSPACEID && !w->isHidden() && !w->m_bX11ShouldntFocus && + !w->m_sAdditionalConfigData.noFocus && w.get() != pIgnoreWindow) { if (w->hasPopupAt(pos)) return w.get(); } @@ -725,9 +804,9 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW if (special != isWorkspaceSpecial(w->m_iWorkspaceID)) continue; - CBox box = {w->m_vPosition, w->m_vSize}; - if (!w->m_bIsFloating && w->m_bIsMapped && box.containsPoint(pos) && w->m_iWorkspaceID == WORKSPACEID && !w->isHidden() && !w->m_bX11ShouldntFocus && !w->m_bNoFocus && - w.get() != pIgnoreWindow) + CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_vPosition, w->m_vSize}; + if (!w->m_bIsFloating && w->m_bIsMapped && box.containsPoint(pos) && w->m_iWorkspaceID == WORKSPACEID && !w->isHidden() && !w->m_bX11ShouldntFocus && + !w->m_sAdditionalConfigData.noFocus && w.get() != pIgnoreWindow) return w.get(); } @@ -735,68 +814,17 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos, CWindow* pIgnoreW }; // special workspace - if (PMONITOR->specialWorkspaceID) + if (PMONITOR->specialWorkspaceID && !**PSPECIALFALLTHRU) return windowForWorkspace(true); - return windowForWorkspace(false); -} - -CWindow* CCompositor::windowFromCursor() { - const auto PMONITOR = getMonitorFromCursor(); - if (PMONITOR->specialWorkspaceID) { - for (auto& w : m_vWindows | std::views::reverse) { - CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; - if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && - !w->isHidden() && !w->m_bNoFocus) - return w.get(); - } + const auto PWINDOW = windowForWorkspace(true); - for (auto& w : m_vWindows) { - CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y}; - if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && !w->m_bNoFocus) - return w.get(); - } + if (PWINDOW) + return PWINDOW; } - // pinned - for (auto& w : m_vWindows | std::views::reverse) { - CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && w->m_bPinned && !w->m_bNoFocus) - return w.get(); - } - - // first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter. - for (auto& w : m_vWindows | std::views::reverse) { - CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bPinned && !w->m_bNoFocus) - return w.get(); - } - - for (auto& w : m_vWindows) { - CBox box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y}; - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bNoFocus) - return w.get(); - } - - return nullptr; -} - -CWindow* CCompositor::windowFloatingFromCursor() { - for (auto& w : m_vWindows | std::views::reverse) { - CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned && !w->m_bNoFocus) - return w.get(); - } - - for (auto& w : m_vWindows | std::views::reverse) { - CBox box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; - if (box.containsPoint({m_sWLRCursor->x, m_sWLRCursor->y}) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && - !w->m_bPinned && !w->m_bNoFocus) - return w.get(); - } - - return nullptr; + return windowForWorkspace(false); } wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pWindow, Vector2D& sl) { @@ -874,15 +902,31 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) { return nullptr; } +CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) { + for (auto& m : m_vRealMonitors) { + if (m->output == out) { + return m.get(); + } + } + + return nullptr; +} + void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; + static auto* const PFOLLOWMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:follow_mouse"); + static auto* const PSPECIALFALLTHROUGH = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:special_fallthrough"); if (g_pCompositor->m_sSeat.exclusiveClient) { Debug::log(LOG, "Disallowing setting focus to a window due to there being an active input inhibitor layer."); return; } + if (!g_pInputManager->m_dExclusiveLSes.empty()) { + Debug::log(LOG, "Refusing a keyboard focus to a window because of an exclusive ls"); + return; + } + g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow); if (!pWindow || !windowValidMapped(pWindow)) { @@ -917,7 +961,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { return; } - if (pWindow->m_bNoFocus) { + if (pWindow->m_sAdditionalConfigData.noFocus) { Debug::log(LOG, "Ignoring focus to nofocus window!"); return; } @@ -928,12 +972,13 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { if (pWindow->m_bPinned) pWindow->m_iWorkspaceID = m_pLastMonitor->activeWorkspace; + const auto PMONITOR = getMonitorFromID(pWindow->m_iMonitorID); + if (!isWorkspaceVisible(pWindow->m_iWorkspaceID)) { + const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID); // This is to fix incorrect feedback on the focus history. - const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID); PWORKSPACE->m_pLastFocusedWindow = pWindow; PWORKSPACE->rememberPrevWorkspace(getWorkspaceByID(m_pLastMonitor->activeWorkspace)); - const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID); PMONITOR->changeWorkspace(PWORKSPACE, false, true); // changeworkspace already calls focusWindow return; @@ -942,6 +987,11 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { const auto PLASTWINDOW = m_pLastWindow; m_pLastWindow = pWindow; + /* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which + window focuses are "via keybinds" and which ones aren't. */ + if (PMONITOR->specialWorkspaceID && PMONITOR->specialWorkspaceID != pWindow->m_iWorkspaceID && !pWindow->m_bPinned && !**PSPECIALFALLTHROUGH) + PMONITOR->setSpecialWorkspace(nullptr); + // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window if (windowValidMapped(PLASTWINDOW)) { PLASTWINDOW->updateDynamicRules(); @@ -1006,7 +1056,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1); } - if (*PFOLLOWMOUSE == 0) + if (**PFOLLOWMOUSE == 0) g_pInputManager->sendMotionEventsToFocused(); } @@ -1015,10 +1065,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) { if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr())) return; // Don't focus when already focused on this. - if (g_pSessionLockManager->isSessionLocked()) { - wlr_seat_keyboard_clear_focus(m_sSeat.seat); - m_pLastFocus = nullptr; - } + if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) + return; // Unfocus last surface if should if (m_pLastFocus && !pWindowOwner) @@ -1089,14 +1137,6 @@ wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector< auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y); - if (ls->layerSurface->current.keyboard_interactive && ls->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { - if (!SURFACEAT) - SURFACEAT = ls->layerSurface->surface; - - *ppLayerSurfaceFound = ls.get(); - return SURFACEAT; - } - if (SURFACEAT) { if (!pixman_region32_not_empty(&SURFACEAT->input_region)) continue; @@ -1483,7 +1523,7 @@ CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) { return nullptr; // 0 -> history, 1 -> shared length - static auto* const PMETHOD = &g_pConfigManager->getConfigValuePtr("binds:focus_preferred_method")->intValue; + static auto* const PMETHOD = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:focus_preferred_method"); const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); @@ -1545,7 +1585,7 @@ CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) { break; } - if (*PMETHOD == 0 /* history */) { + if (**PMETHOD == 0 /* history */) { if (intersectLength > 0) { // get idx @@ -1639,7 +1679,7 @@ CWindow* CCompositor::getNextWindowOnWorkspace(CWindow* pWindow, bool focusableO if (floating.has_value() && w->m_bIsFloating != floating.value()) continue; - if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus)) + if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sAdditionalConfigData.noFocus)) return w.get(); } @@ -1647,7 +1687,7 @@ CWindow* CCompositor::getNextWindowOnWorkspace(CWindow* pWindow, bool focusableO if (floating.has_value() && w->m_bIsFloating != floating.value()) continue; - if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus)) + if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sAdditionalConfigData.noFocus)) return w.get(); } @@ -1668,7 +1708,7 @@ CWindow* CCompositor::getPrevWindowOnWorkspace(CWindow* pWindow, bool focusableO if (floating.has_value() && w->m_bIsFloating != floating.value()) continue; - if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus)) + if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sAdditionalConfigData.noFocus)) return w.get(); } @@ -1676,7 +1716,7 @@ CWindow* CCompositor::getPrevWindowOnWorkspace(CWindow* pWindow, bool focusableO if (floating.has_value() && w->m_bIsFloating != floating.value()) continue; - if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_bNoFocus)) + if (w.get() != pWindow && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sAdditionalConfigData.noFocus)) return w.get(); } @@ -1764,8 +1804,15 @@ CWindow* CCompositor::getConstraintWindow(SMouse* pMouse) { } CMonitor* CCompositor::getMonitorInDirection(const char& dir) { - const auto POSA = m_pLastMonitor->vecPosition; - const auto SIZEA = m_pLastMonitor->vecSize; + return this->getMonitorInDirection(m_pLastMonitor, dir); +} + +CMonitor* CCompositor::getMonitorInDirection(CMonitor* pSourceMonitor, const char& dir) { + if (!pSourceMonitor) + return nullptr; + + const auto POSA = pSourceMonitor->vecPosition; + const auto SIZEA = pSourceMonitor->vecSize; auto longestIntersect = -1; CMonitor* longestIntersectMonitor = nullptr; @@ -1844,21 +1891,30 @@ void CCompositor::updateWorkspaceWindows(const int64_t& id) { void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { // optimization - static auto* const ACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.active_border")->data.get(); - static auto* const INACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->data.get(); - static auto* const NOGROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border_active")->data.get(); - static auto* const NOGROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border")->data.get(); - static auto* const GROUPACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:col.border_active")->data.get(); - static auto* const GROUPINACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:col.border_inactive")->data.get(); - static auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:col.border_locked_active")->data.get(); - static auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("group:col.border_locked_inactive")->data.get(); - static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue; - static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue; - static auto* const PFULLSCREENALPHA = &g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity")->floatValue; - static auto* const PSHADOWCOL = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow")->intValue; - static auto* const PSHADOWCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive")->intValue; - static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue; - static auto* const PDIMENABLED = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue; + static auto* const PACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:col.active_border"); + static auto* const PINACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border"); + static auto* const PNOGROUPACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border_active"); + static auto* const PNOGROUPINACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:col.nogroup_border"); + static auto* const PGROUPACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:col.border_active"); + static auto* const PGROUPINACTIVECOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:col.border_inactive"); + static auto* const PGROUPACTIVELOCKEDCOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:col.border_locked_active"); + static auto* const PGROUPINACTIVELOCKEDCOL = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:col.border_locked_inactive"); + static auto* const PINACTIVEALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity"); + static auto* const PACTIVEALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:active_opacity"); + static auto* const PFULLSCREENALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity"); + static auto* const PSHADOWCOL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:col.shadow"); + static auto* const PSHADOWCOLINACTIVE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive"); + static auto* const PDIMSTRENGTH = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_strength"); + static auto* const PDIMENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_inactive"); + + auto* const ACTIVECOL = (CGradientValueData*)(*PACTIVECOL)->getData(); + auto* const INACTIVECOL = (CGradientValueData*)(*PINACTIVECOL)->getData(); + auto* const NOGROUPACTIVECOL = (CGradientValueData*)(*PNOGROUPACTIVECOL)->getData(); + auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(*PNOGROUPINACTIVECOL)->getData(); + auto* const GROUPACTIVECOL = (CGradientValueData*)(*PGROUPACTIVECOL)->getData(); + auto* const GROUPINACTIVECOL = (CGradientValueData*)(*PGROUPINACTIVECOL)->getData(); + auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(*PGROUPACTIVELOCKEDCOL)->getData(); + auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(*PGROUPINACTIVELOCKEDCOL)->getData(); auto setBorderColor = [&](CGradientValueData grad) -> void { if (grad == pWindow->m_cRealBorderColor) @@ -1896,31 +1952,31 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { // opacity const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); if (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) { - pWindow->m_fActiveInactiveAlpha = *PFULLSCREENALPHA; + pWindow->m_fActiveInactiveAlpha = **PFULLSCREENALPHA; } else { if (pWindow == m_pLastWindow) pWindow->m_fActiveInactiveAlpha = pWindow->m_sSpecialRenderData.alphaOverride.toUnderlying() ? pWindow->m_sSpecialRenderData.alpha.toUnderlying() : - pWindow->m_sSpecialRenderData.alpha.toUnderlying() * *PACTIVEALPHA; + pWindow->m_sSpecialRenderData.alpha.toUnderlying() * **PACTIVEALPHA; else pWindow->m_fActiveInactiveAlpha = pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() != -1 ? (pWindow->m_sSpecialRenderData.alphaInactiveOverride.toUnderlying() ? pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() : - pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() * *PINACTIVEALPHA) : - *PINACTIVEALPHA; + pWindow->m_sSpecialRenderData.alphaInactive.toUnderlying() * **PINACTIVEALPHA) : + **PINACTIVEALPHA; } // dim - if (pWindow == m_pLastWindow || pWindow->m_sAdditionalConfigData.forceNoDim || !*PDIMENABLED) { + if (pWindow == m_pLastWindow || pWindow->m_sAdditionalConfigData.forceNoDim || !**PDIMENABLED) { pWindow->m_fDimPercent = 0; } else { - pWindow->m_fDimPercent = *PDIMSTRENGTH; + pWindow->m_fDimPercent = **PDIMSTRENGTH; } // shadow if (pWindow->m_iX11Type != 2 && !pWindow->m_bX11DoesntWantBorders) { if (pWindow == m_pLastWindow) { - pWindow->m_cRealShadowColor = CColor(*PSHADOWCOL); + pWindow->m_cRealShadowColor = CColor(**PSHADOWCOL); } else { - pWindow->m_cRealShadowColor = CColor(*PSHADOWCOLINACTIVE != INT_MAX ? *PSHADOWCOLINACTIVE : *PSHADOWCOL); + pWindow->m_cRealShadowColor = CColor(**PSHADOWCOLINACTIVE != INT_MAX ? **PSHADOWCOLINACTIVE : **PSHADOWCOL); } } else { pWindow->m_cRealShadowColor.setValueAndWarp(CColor(0, 0, 0, 0)); // no shadow @@ -2012,7 +2068,11 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB) updateFullscreenFadeOnWorkspace(PWORKSPACEB); updateFullscreenFadeOnWorkspace(PWORKSPACEA); - g_pInputManager->sendMotionEventsToFocused(); + if (pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID || pMonitorB->ID == g_pCompositor->m_pLastMonitor->ID) { + const auto LASTWIN = pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID ? PWORKSPACEB->getLastFocusedWindow() : PWORKSPACEA->getLastFocusedWindow(); + g_pCompositor->focusWindow(LASTWIN ? LASTWIN : + (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING))); + } // event g_pEventManager->postEvent(SHyprIPCEvent{"moveworkspace", PWORKSPACEA->m_szName + "," + pMonitorB->szName}); @@ -2022,7 +2082,11 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB) } CMonitor* CCompositor::getMonitorFromString(const std::string& name) { - if (name[0] == '+' || name[0] == '-') { + if (name == "current") + return g_pCompositor->m_pLastMonitor; + else if (isDirection(name)) + return getMonitorInDirection(name[0]); + else if (name[0] == '+' || name[0] == '-') { // relative if (m_vMonitors.size() == 1) @@ -2077,39 +2141,21 @@ CMonitor* CCompositor::getMonitorFromString(const std::string& name) { Debug::log(ERR, "Error in getMonitorFromString: invalid arg 1"); return nullptr; } - } else if (name.starts_with("desc:")) { - const auto DESCRIPTION = name.substr(5); - + } else { for (auto& m : m_vMonitors) { if (!m->output) continue; - if (m->szDescription.starts_with(DESCRIPTION)) { + if (m->matchesStaticSelector(name)) { return m.get(); } } - - return nullptr; - } else { - if (name == "current") - return g_pCompositor->m_pLastMonitor; - - if (isDirection(name)) { - const auto PMONITOR = getMonitorInDirection(name[0]); - return PMONITOR; - } else { - for (auto& m : m_vMonitors) { - if (m->szName == name) { - return m.get(); - } - } - } } return nullptr; } -void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor) { +void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMonitor, bool noWarpCursor) { // We trust the workspace and monitor to be correct. @@ -2124,7 +2170,7 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni // fix old mon int nextWorkspaceOnMonitorID = -1; - if (!SWITCHINGISACTIVE) + if (!SWITCHINGISACTIVE || !POLDMON) nextWorkspaceOnMonitorID = pWorkspace->m_iID; else { for (auto& w : m_vWorkspaces) { @@ -2149,7 +2195,7 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni } Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID); - POLDMON->changeWorkspace(nextWorkspaceOnMonitorID); + POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true); } // move the workspace @@ -2190,12 +2236,14 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni if (const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace); PWORKSPACE) getWorkspaceByID(pMonitor->activeWorkspace)->startAnim(false, false); + setActiveMonitor(pMonitor); pMonitor->activeWorkspace = pWorkspace->m_iID; g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); pWorkspace->startAnim(true, true, true); - wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2); + if (!noWarpCursor) + wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2); g_pInputManager->sendMotionEventsToFocused(); } @@ -2282,7 +2330,6 @@ void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode g_pXWaylandManager->setWindowFullscreen(pWindow, pWindow->shouldSendFullscreenState()); - pWindow->updateDynamicRules(); updateWindowAnimatedDecorationValues(pWindow); // make all windows on the same workspace under the fullscreen window @@ -2415,9 +2462,9 @@ void CCompositor::warpCursorTo(const Vector2D& pos, bool force) { // warpCursorTo should only be used for warps that // should be disabled with no_cursor_warps - static auto* const PNOWARPS = &g_pConfigManager->getConfigValuePtr("general:no_cursor_warps")->intValue; + static auto* const PNOWARPS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:no_cursor_warps"); - if (*PNOWARPS && !force) + if (**PNOWARPS && !force) return; if (!m_sSeat.mouse) @@ -2583,7 +2630,7 @@ void CCompositor::setActiveMonitor(CMonitor* pMonitor) { const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace); - g_pEventManager->postEvent(SHyprIPCEvent{"focusedmon", pMonitor->szName + "," + PWORKSPACE->m_szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"focusedmon", pMonitor->szName + "," + (PWORKSPACE ? PWORKSPACE->m_szName : "?")}); EMIT_HOOK_EVENT("focusedMon", pMonitor); m_pLastMonitor = pMonitor; } @@ -2604,11 +2651,7 @@ int CCompositor::getNewSpecialID() { } void CCompositor::performUserChecks() { - if (g_pConfigManager->getInt("general:allow_tearing") == 1 && !envEnabled("WLR_DRM_NO_ATOMIC")) { - g_pHyprNotificationOverlay->addNotification("You have enabled tearing, but immediate presentations are not available on your configuration. Try adding " - "env = WLR_DRM_NO_ATOMIC,1 to your config.", - CColor(0), 15000, ICON_WARNING); - } + ; // intentional } void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) { @@ -2684,7 +2727,7 @@ void CCompositor::setIdleActivityInhibit(bool enabled) { wlr_idle_notifier_v1_set_inhibited(g_pCompositor->m_sWLRIdleNotifier, !enabled); } void CCompositor::arrangeMonitors() { - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); std::vector toArrange; std::vector arranged; @@ -2733,9 +2776,9 @@ void CCompositor::arrangeMonitors() { for (auto& m : m_vMonitors) { Debug::log(LOG, "arrangeMonitors: {} xwayland [{}, {:.2f}]", m->szName, maxOffset, 0.f); m->vecXWaylandPosition = {maxOffset, 0}; - maxOffset += (*PXWLFORCESCALEZERO ? m->vecTransformedSize.x : m->vecSize.x); + maxOffset += (**PXWLFORCESCALEZERO ? m->vecTransformedSize.x : m->vecSize.x); - if (*PXWLFORCESCALEZERO) + if (**PXWLFORCESCALEZERO) m->xwaylandScale = m->scale; else m->xwaylandScale = 1.f; @@ -2783,10 +2826,27 @@ void CCompositor::leaveUnsafeState() { void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) { g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(pSurface, scale); wlr_surface_set_preferred_buffer_scale(pSurface, static_cast(std::ceil(scale))); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + if (!PSURFACE) { + Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface); + return; + } + + PSURFACE->m_fLastScale = scale; + PSURFACE->m_iLastScale = static_cast(std::ceil(scale)); } void CCompositor::setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform) { wlr_surface_set_preferred_buffer_transform(pSurface, transform); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + if (!PSURFACE) { + Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface); + return; + } + + PSURFACE->m_eLastTransform = transform; } void CCompositor::updateSuspendedStates() { diff --git a/src/Compositor.hpp b/src/Compositor.hpp index f954526c..c08a5931 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -135,15 +135,14 @@ class CCompositor { void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr); bool windowExists(CWindow*); bool windowValidMapped(CWindow*); - CWindow* vectorToWindowIdeal(const Vector2D&, CWindow* pIgnoreWindow = nullptr); // used only for finding a window to focus on, basically a "findFocusableWindow" - CWindow* vectorToWindowTiled(const Vector2D&); + bool monitorExists(CMonitor*); + CWindow* vectorToWindowUnified(const Vector2D&, uint8_t properties, CWindow* pIgnoreWindow = nullptr); wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector>*, Vector2D*, SLayerSurface**); SIMEPopup* vectorToIMEPopup(const Vector2D& pos, std::list& popups); wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl); Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*); - CWindow* windowFromCursor(); - CWindow* windowFloatingFromCursor(); CMonitor* getMonitorFromOutput(wlr_output*); + CMonitor* getRealMonitorFromOutput(wlr_output*); CWindow* getWindowForPopup(wlr_xdg_popup*); CWindow* getWindowFromSurface(wlr_surface*); CWindow* getWindowFromHandle(uint32_t); @@ -172,11 +171,12 @@ class CCompositor { bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr); CWindow* getConstraintWindow(SMouse*); CMonitor* getMonitorInDirection(const char&); + CMonitor* getMonitorInDirection(CMonitor*, const char&); void updateAllWindowsAnimatedDecorationValues(); void updateWorkspaceWindows(const int64_t& id); void updateWindowAnimatedDecorationValues(CWindow*); int getNextAvailableMonitorID(std::string const& name); - void moveWorkspaceToMonitor(CWorkspace*, CMonitor*); + void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false); void swapActiveWorkspaces(CMonitor*, CMonitor*); CMonitor* getMonitorFromString(const std::string&); bool workspaceIDOutOfBounds(const int64_t&); @@ -214,6 +214,7 @@ class CCompositor { private: void initAllSignals(); + void removeAllSignals(); void setRandomSplash(); void initManagers(eManagersInitStage stage); void prepareFallbackOutput(); diff --git a/src/SharedDefs.hpp b/src/SharedDefs.hpp index da7f6938..55ce1f81 100644 --- a/src/SharedDefs.hpp +++ b/src/SharedDefs.hpp @@ -1,6 +1,7 @@ #pragma once #include "helpers/Vector2D.hpp" +#include enum eIcons { ICON_WARNING = 0, @@ -52,4 +53,20 @@ struct SWindowDecorationExtents { bool operator==(const SWindowDecorationExtents& other) const { return topLeft == other.topLeft && bottomRight == other.bottomRight; } -}; \ No newline at end of file + + void addExtents(const SWindowDecorationExtents& other) { + topLeft = topLeft.getComponentMax(other.topLeft); + bottomRight = bottomRight.getComponentMax(other.bottomRight); + } +}; + +enum eHyprCtlOutputFormat { + FORMAT_NORMAL = 0, + FORMAT_JSON +}; + +struct SHyprCtlCommand { + std::string name = ""; + bool exact = true; + std::function fn; +}; diff --git a/src/Window.cpp b/src/Window.cpp index 978d1000..ece5b658 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -139,35 +139,25 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; } -CBox CWindow::getWindowInputBox() { - const int BORDERSIZE = getRealBorderSize(); +CBox CWindow::getWindowBoxUnified(uint64_t properties) { if (m_sAdditionalConfigData.dimAround) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; } - SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}}; + SWindowDecorationExtents EXTENTS = {{0, 0}, {0, 0}}; + if (properties & RESERVED_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(this)); + if (properties & INPUT_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, true)); + if (properties & FULL_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(this, false)); - const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this, true); + CBox box = {m_vRealPosition.vec().x, m_vRealPosition.vec().y, m_vRealSize.vec().x, m_vRealSize.vec().y}; + box.addExtents(EXTENTS); - if (EXTENTS.topLeft.x > maxExtents.topLeft.x) - maxExtents.topLeft.x = EXTENTS.topLeft.x; - - if (EXTENTS.topLeft.y > maxExtents.topLeft.y) - maxExtents.topLeft.y = EXTENTS.topLeft.y; - - if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x) - maxExtents.bottomRight.x = EXTENTS.bottomRight.x; - - if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y) - maxExtents.bottomRight.y = EXTENTS.bottomRight.y; - - // Add extents to the real base BB and return - CBox finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y, - m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y}; - - return finalBox; + return box; } CBox CWindow::getWindowMainSurfaceBox() { @@ -318,7 +308,7 @@ void CWindow::destroyToplevelHandle() { } void CWindow::updateToplevel() { - updateSurfaceOutputs(); + updateSurfaceScaleTransformDetails(); if (!m_phForeignToplevel) return; @@ -345,8 +335,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) { wlr_surface_send_leave(pSurface, OUTPUT); } -void CWindow::updateSurfaceOutputs() { - if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden) +void CWindow::updateSurfaceScaleTransformDetails() { + if (!m_bIsMapped || m_bHidden) return; const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID); @@ -355,16 +345,23 @@ void CWindow::updateSurfaceOutputs() { const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); + if (PNEWMONITOR != PLASTMONITOR) { + if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) + wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output); + wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output); + } wlr_surface_for_each_surface( m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID); - g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR ? PMONITOR->scale : 1.f); + + const auto PSURFACE = CWLSurface::surfaceFromWlr(surf); + if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale) + return; + + g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR->scale); g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform); }, this); @@ -374,7 +371,7 @@ void CWindow::moveToWorkspace(int workspaceID) { if (m_iWorkspaceID == workspaceID) return; - static auto* const PCLOSEONLASTSPECIAL = &g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty")->intValue; + static auto* const PCLOSEONLASTSPECIAL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty"); const int OLDWORKSPACE = m_iWorkspaceID; @@ -397,7 +394,7 @@ void CWindow::moveToWorkspace(int workspaceID) { // update xwayland coords g_pXWaylandManager->setWindowSize(this, m_vRealSize.vec()); - if (g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE) == 0 && *PCLOSEONLASTSPECIAL) { + if (g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE) == 0 && **PCLOSEONLASTSPECIAL) { const auto PWS = g_pCompositor->getWorkspaceByID(OLDWORKSPACE); if (PWS) { @@ -440,7 +437,7 @@ void unregisterVar(void* ptr) { } void CWindow::onUnmap() { - static auto* const PCLOSEONLASTSPECIAL = &g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty")->intValue; + static auto* const PCLOSEONLASTSPECIAL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty"); if (g_pCompositor->m_pLastWindow == this) g_pCompositor->m_pLastWindow = nullptr; @@ -462,7 +459,7 @@ void CWindow::onUnmap() { hyprListener_unmapWindow.removeCallback(); - if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(m_iWorkspaceID) == 0 && g_pCompositor->isWorkspaceSpecial(m_iWorkspaceID)) { + if (**PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(m_iWorkspaceID) == 0 && g_pCompositor->isWorkspaceSpecial(m_iWorkspaceID)) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID) PMONITOR->setSpecialWorkspace(nullptr); @@ -511,6 +508,14 @@ void CWindow::onMap() { "CWindow"); m_vReportedSize = m_vPendingReportedSize; + + for (const auto& ctrl : g_pHyprRenderer->m_vTearingControllers) { + if (ctrl->pWlrHint->surface != m_pWLSurface.wlr()) + continue; + + m_bTearingHint = ctrl->pWlrHint->current; + break; + } } void CWindow::onBorderAngleAnimEnd(void* ptr) { @@ -625,23 +630,22 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { if (active && token.contains("deg")) { activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); active = false; - } else if (token.contains("deg")) { + } else if (token.contains("deg")) inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); - } else if (active) { + else if (active) activeBorderGradient.m_vColors.push_back(configStringToInt(token)); - } else { + else inactiveBorderGradient.m_vColors.push_back(configStringToInt(token)); - } } - // Includes sanity checks for the number of colors in each gradient - if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10) { + // Includes sanity checks for the number of colors in each gradient + if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10) Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule); - } else if (activeBorderGradient.m_vColors.empty()) { + else if (activeBorderGradient.m_vColors.empty()) Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule); - } else if (inactiveBorderGradient.m_vColors.empty()) { + else if (inactiveBorderGradient.m_vColors.empty()) m_sSpecialRenderData.activeBorderColor = activeBorderGradient; - } else { + else { m_sSpecialRenderData.activeBorderColor = activeBorderGradient; m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient; } @@ -1019,19 +1023,21 @@ bool CWindow::opaque() { } float CWindow::rounding() { - static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue; + static auto* const PROUNDING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:rounding"); - float rounding = m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_sAdditionalConfigData.rounding.toUnderlying(); + float rounding = m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? **PROUNDING : m_sAdditionalConfigData.rounding.toUnderlying(); return m_sSpecialRenderData.rounding ? rounding : 0; } void CWindow::updateSpecialRenderData() { - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID); - const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{}; - bool border = true; + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID); + const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{}; + bool border = true; - if (m_bIsFloating && g_pConfigManager->getConfigValuePtr("general:no_border_on_floating")->intValue == 1) + static auto* const* PNOBORDERONFLOATING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:no_border_on_floating"); + + if (m_bIsFloating && **PNOBORDERONFLOATING == 1) border = false; m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(border); @@ -1051,16 +1057,18 @@ int CWindow::getRealBorderSize() { if (m_sSpecialRenderData.borderSize.toUnderlying() != -1) return m_sSpecialRenderData.borderSize.toUnderlying(); - return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; + static auto* const* PBORDERSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:border_size"); + + return **PBORDERSIZE; } bool CWindow::canBeTorn() { - return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied; + return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint); } bool CWindow::shouldSendFullscreenState() { const auto MODE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID)->m_efFullscreenMode; - return m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL)); + return m_bDontSendFullscreen ? false : (m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL))); } void CWindow::setSuspended(bool suspend) { diff --git a/src/Window.hpp b/src/Window.hpp index 93d7f203..f8ba4180 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -30,6 +30,24 @@ enum eGroupRules { GROUP_OVERRIDE = 1 << 6, // Override other rules }; +enum eGetWindowProperties { + WINDOW_ONLY = 0, + RESERVED_EXTENTS = 1 << 0, + INPUT_EXTENTS = 1 << 1, + FULL_EXTENTS = 1 << 2, + FLOATING_ONLY = 1 << 3, + ALLOW_FLOATING = 1 << 4, + USE_PROP_TILED = 1 << 5, +}; + +enum eSuppressEvents { + SUPPRESS_NONE = 0, + SUPPRESS_FULLSCREEN = 1 << 0, + SUPPRESS_MAXIMIZE = 1 << 1, + SUPPRESS_ACTIVATE = 1 << 2, + SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3, +}; + class IWindowTransformer; template @@ -133,6 +151,7 @@ struct SWindowAdditionalConfigData { CWindowOverridableVar forceNoBorder = false; CWindowOverridableVar forceNoShadow = false; CWindowOverridableVar forceNoDim = false; + CWindowOverridableVar noFocus = false; CWindowOverridableVar windowDanceCompat = false; CWindowOverridableVar noMaxSize = false; CWindowOverridableVar dimAround = false; @@ -221,16 +240,17 @@ class CWindow { bool m_bIsPseudotiled = false; Vector2D m_vPseudoSize = Vector2D(0, 0); - bool m_bFirstMap = false; // for layouts - bool m_bIsFloating = false; - bool m_bDraggingTiled = false; // for dragging around tiled windows - bool m_bIsFullscreen = false; - bool m_bWasMaximized = false; - uint64_t m_iMonitorID = -1; - std::string m_szTitle = ""; - std::string m_szInitialTitle = ""; - std::string m_szInitialClass = ""; - int m_iWorkspaceID = -1; + bool m_bFirstMap = false; // for layouts + bool m_bIsFloating = false; + bool m_bDraggingTiled = false; // for dragging around tiled windows + bool m_bIsFullscreen = false; + bool m_bDontSendFullscreen = false; + bool m_bWasMaximized = false; + uint64_t m_iMonitorID = -1; + std::string m_szTitle = ""; + std::string m_szInitialTitle = ""; + std::string m_szInitialClass = ""; + int m_iWorkspaceID = -1; bool m_bIsMapped = false; @@ -250,13 +270,13 @@ class CWindow { // // For nofocus - bool m_bNoFocus = false; bool m_bNoInitialFocus = false; // Fullscreen and Maximize - bool m_bWantsInitialFullscreen = false; - bool m_bNoFullscreenRequest = false; - bool m_bNoMaximizeRequest = false; + bool m_bWantsInitialFullscreen = false; + + // bitfield eSuppressEvents + uint64_t m_eSuppressedEvents = SUPPRESS_NONE; SSurfaceTreeNode* m_pSurfaceTree = nullptr; @@ -342,7 +362,7 @@ class CWindow { // methods CBox getFullWindowBoundingBox(); SWindowDecorationExtents getFullWindowExtents(); - CBox getWindowInputBox(); + CBox getWindowBoxUnified(uint64_t props); CBox getWindowMainSurfaceBox(); CBox getWindowIdealBoundingBoxIgnoreReserved(); void addWindowDeco(std::unique_ptr deco); @@ -356,7 +376,7 @@ class CWindow { void createToplevelHandle(); void destroyToplevelHandle(); void updateToplevel(); - void updateSurfaceOutputs(); + void updateSurfaceScaleTransformDetails(); void moveToWorkspace(int); CWindow* X11TransientFor(); void onUnmap(); diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp index 14b5e51d..accc92ab 100644 --- a/src/config/ConfigDataValues.hpp +++ b/src/config/ConfigDataValues.hpp @@ -1,10 +1,12 @@ #pragma once #include "../defines.hpp" +#include "../helpers/VarList.hpp" #include enum eConfigValueDataTypes { - CVD_TYPE_INVALID = -1, - CVD_TYPE_GRADIENT = 0 + CVD_TYPE_INVALID = -1, + CVD_TYPE_GRADIENT = 0, + CVD_TYPE_CSS_VALUE = 1 }; class ICustomConfigValueData { @@ -50,3 +52,55 @@ class CGradientValueData : public ICustomConfigValueData { return true; } }; + +class CCssGapData : public ICustomConfigValueData { + public: + CCssGapData() : top(0), right(0), bottom(0), left(0){}; + CCssGapData(int64_t global) : top(global), right(global), bottom(global), left(global){}; + CCssGapData(int64_t vertical, int64_t horizontal) : top(vertical), right(horizontal), bottom(vertical), left(horizontal){}; + CCssGapData(int64_t top, int64_t horizontal, int64_t bottom) : top(top), right(horizontal), bottom(bottom), left(horizontal){}; + CCssGapData(int64_t top, int64_t right, int64_t bottom, int64_t left) : top(top), right(right), bottom(bottom), left(left){}; + + /* Css like directions */ + int64_t top; + int64_t right; + int64_t bottom; + int64_t left; + + void parseGapData(CVarList varlist) { + switch (varlist.size()) { + case 1: { + *this = CCssGapData(std::stoi(varlist[0])); + break; + } + case 2: { + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1])); + break; + } + case 3: { + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2])); + break; + } + case 4: { + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3])); + break; + } + default: { + Debug::log(WARN, "Too many arguments provided for gaps."); + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3])); + break; + } + } + } + + void reset(int64_t global) { + top = global; + right = global; + bottom = global; + left = global; + } + + virtual eConfigValueDataTypes getDataType() { + return CVD_TYPE_CSS_VALUE; + } +}; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index ee198ded..5cb18e0f 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -2,6 +2,8 @@ #include "../managers/KeybindManager.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "config/ConfigDataValues.hpp" +#include "helpers/VarList.hpp" #include #include @@ -15,35 +17,575 @@ #include #include -extern "C" char** environ; +extern "C" char** environ; + +static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) { + std::string V = VALUE; + + if (!*data) + *data = new CGradientValueData(); + + const auto DATA = reinterpret_cast(*data); + + CVarList varlist(V, 0, ' '); + DATA->m_vColors.clear(); + + std::string parseError = ""; + + for (auto& var : varlist) { + if (var.find("deg") != std::string::npos) { + // last arg + try { + DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians + } catch (...) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V; + } + + break; + } + + if (DATA->m_vColors.size() >= 10) { + Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V); + parseError = "Error parsing gradient " + V + ": max colors is 10."; + break; + } + + try { + DATA->m_vColors.push_back(CColor(configStringToInt(var))); + } catch (std::exception& e) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V + ": " + e.what(); + } + } + + if (DATA->m_vColors.size() == 0) { + Debug::log(WARN, "Error parsing gradient {}", V); + parseError = "Error parsing gradient " + V + ": No colors?"; + + DATA->m_vColors.push_back(0); // transparent + } + + Hyprlang::CParseResult result; + if (!parseError.empty()) + result.setError(parseError.c_str()); + + return result; +} + +static void configHandleGradientDestroy(void** data) { + if (*data) + delete reinterpret_cast(*data); +} + +static Hyprlang::CParseResult configHandleGapSet(const char* VALUE, void** data) { + std::string V = VALUE; + + if (!*data) + *data = new CCssGapData(); + + const auto DATA = reinterpret_cast(*data); + CVarList varlist(V); + Hyprlang::CParseResult result; + + try { + DATA->parseGapData(varlist); + } catch (...) { + std::string parseError = "Error parsing gaps " + V; + result.setError(parseError.c_str()); + } + + return result; +} + +static void configHandleGapDestroy(void** data) { + if (*data) + delete reinterpret_cast(*data); +} + +static Hyprlang::CParseResult handleRawExec(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleRawExec(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleExecOnce(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleExecOnce(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleMonitor(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleMonitor(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBezier(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBezier(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleAnimation(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleAnimation(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBind(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBind(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleUnbind(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRuleV2(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWorkspaceRules(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleSubmap(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleSubmap(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleSource(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleSource(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleEnv(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleEnv(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handlePlugin(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} CConfigManager::CConfigManager() { - configValues["general:col.active_border"].data = std::make_shared(0xffffffff); - configValues["general:col.inactive_border"].data = std::make_shared(0xff444444); - configValues["general:col.nogroup_border"].data = std::make_shared(0xffffaaff); - configValues["general:col.nogroup_border_active"].data = std::make_shared(0xffff00ff); + const auto ERR = verifyConfigExists(); - configValues["group:col.border_active"].data = std::make_shared(0x66ffff00); - configValues["group:col.border_inactive"].data = std::make_shared(0x66777700); - configValues["group:col.border_locked_active"].data = std::make_shared(0x66ff5500); - configValues["group:col.border_locked_inactive"].data = std::make_shared(0x66775500); + configPaths.emplace_back(getMainConfigPath()); + m_pConfig = std::make_unique(configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); - configValues["group:groupbar:col.active"].data = std::make_shared(0x66ffff00); - configValues["group:groupbar:col.inactive"].data = std::make_shared(0x66777700); - configValues["group:groupbar:col.locked_active"].data = std::make_shared(0x66ff5500); - configValues["group:groupbar:col.locked_inactive"].data = std::make_shared(0x66775500); + m_pConfig->addConfigValue("general:sensitivity", {1.0f}); + m_pConfig->addConfigValue("general:apply_sens_to_raw", {0L}); + m_pConfig->addConfigValue("general:border_size", {1L}); + m_pConfig->addConfigValue("general:no_border_on_floating", {0L}); + m_pConfig->addConfigValue("general:border_part_of_window", {1L}); + m_pConfig->addConfigValue("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"}); + m_pConfig->addConfigValue("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"}); + m_pConfig->addConfigValue("general:gaps_workspaces", {0L}); + m_pConfig->addConfigValue("general:cursor_inactive_timeout", {0L}); + m_pConfig->addConfigValue("general:no_cursor_warps", {0L}); + m_pConfig->addConfigValue("general:no_focus_fallback", {0L}); + m_pConfig->addConfigValue("general:resize_on_border", {0L}); + m_pConfig->addConfigValue("general:extend_border_grab_area", {15L}); + m_pConfig->addConfigValue("general:hover_icon_on_border", {1L}); + m_pConfig->addConfigValue("general:layout", {"dwindle"}); + m_pConfig->addConfigValue("general:allow_tearing", {0L}); + + m_pConfig->addConfigValue("misc:disable_hyprland_logo", {0L}); + m_pConfig->addConfigValue("misc:disable_splash_rendering", {0L}); + m_pConfig->addConfigValue("misc:col.splash", {0x55ffffffL}); + m_pConfig->addConfigValue("misc:splash_font_family", {"Sans"}); + m_pConfig->addConfigValue("misc:force_default_wallpaper", {-1L}); + m_pConfig->addConfigValue("misc:vfr", {1L}); + m_pConfig->addConfigValue("misc:vrr", {0L}); + m_pConfig->addConfigValue("misc:mouse_move_enables_dpms", {0L}); + m_pConfig->addConfigValue("misc:key_press_enables_dpms", {0L}); + m_pConfig->addConfigValue("misc:always_follow_on_dnd", {1L}); + m_pConfig->addConfigValue("misc:layers_hog_keyboard_focus", {1L}); + m_pConfig->addConfigValue("misc:animate_manual_resizes", {0L}); + m_pConfig->addConfigValue("misc:animate_mouse_windowdragging", {0L}); + m_pConfig->addConfigValue("misc:disable_autoreload", {0L}); + m_pConfig->addConfigValue("misc:enable_swallow", {0L}); + m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("misc:focus_on_activate", {0L}); + m_pConfig->addConfigValue("misc:no_direct_scanout", {1L}); + m_pConfig->addConfigValue("misc:hide_cursor_on_touch", {1L}); + m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", {1L}); + m_pConfig->addConfigValue("misc:render_ahead_of_time", {0L}); + m_pConfig->addConfigValue("misc:render_ahead_safezone", {1L}); + m_pConfig->addConfigValue("misc:cursor_zoom_factor", {1.f}); + m_pConfig->addConfigValue("misc:cursor_zoom_rigid", {0L}); + m_pConfig->addConfigValue("misc:allow_session_lock_restore", {0L}); + m_pConfig->addConfigValue("misc:close_special_on_empty", {1L}); + m_pConfig->addConfigValue("misc:background_color", {0xff111111L}); + m_pConfig->addConfigValue("misc:new_window_takes_over_fullscreen", {0L}); + + m_pConfig->addConfigValue("group:insert_after_current", {1L}); + m_pConfig->addConfigValue("group:focus_removed_window", {1L}); + m_pConfig->addConfigValue("group:groupbar:enabled", {1L}); + m_pConfig->addConfigValue("group:groupbar:font_family", {"Sans"}); + m_pConfig->addConfigValue("group:groupbar:font_size", {8L}); + m_pConfig->addConfigValue("group:groupbar:gradients", {1L}); + m_pConfig->addConfigValue("group:groupbar:height", {14L}); + m_pConfig->addConfigValue("group:groupbar:priority", {3L}); + m_pConfig->addConfigValue("group:groupbar:render_titles", {1L}); + m_pConfig->addConfigValue("group:groupbar:scrolling", {1L}); + m_pConfig->addConfigValue("group:groupbar:text_color", {0xffffffffL}); + + m_pConfig->addConfigValue("debug:int", {0L}); + m_pConfig->addConfigValue("debug:log_damage", {0L}); + m_pConfig->addConfigValue("debug:overlay", {0L}); + m_pConfig->addConfigValue("debug:damage_blink", {0L}); + m_pConfig->addConfigValue("debug:disable_logs", {1L}); + m_pConfig->addConfigValue("debug:disable_time", {1L}); + m_pConfig->addConfigValue("debug:enable_stdout_logs", {0L}); + m_pConfig->addConfigValue("debug:damage_tracking", {(Hyprlang::INT)DAMAGE_TRACKING_FULL}); + m_pConfig->addConfigValue("debug:manual_crash", {0L}); + m_pConfig->addConfigValue("debug:suppress_errors", {0L}); + m_pConfig->addConfigValue("debug:watchdog_timeout", {5L}); + m_pConfig->addConfigValue("debug:disable_scale_checks", {0L}); + + m_pConfig->addConfigValue("decoration:rounding", {0L}); + m_pConfig->addConfigValue("decoration:blur:enabled", {1L}); + m_pConfig->addConfigValue("decoration:blur:size", {8L}); + m_pConfig->addConfigValue("decoration:blur:passes", {1L}); + m_pConfig->addConfigValue("decoration:blur:ignore_opacity", {0L}); + m_pConfig->addConfigValue("decoration:blur:new_optimizations", {1L}); + m_pConfig->addConfigValue("decoration:blur:xray", {0L}); + m_pConfig->addConfigValue("decoration:blur:contrast", {0.8916F}); + m_pConfig->addConfigValue("decoration:blur:brightness", {1.0F}); + m_pConfig->addConfigValue("decoration:blur:vibrancy", {0.1696F}); + m_pConfig->addConfigValue("decoration:blur:vibrancy_darkness", {0.0F}); + m_pConfig->addConfigValue("decoration:blur:noise", {0.0117F}); + m_pConfig->addConfigValue("decoration:blur:special", {0L}); + m_pConfig->addConfigValue("decoration:blur:popups", {0L}); + m_pConfig->addConfigValue("decoration:blur:popups_ignorealpha", {0.2F}); + m_pConfig->addConfigValue("decoration:active_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:inactive_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:fullscreen_opacity", {1.F}); + m_pConfig->addConfigValue("decoration:no_blur_on_oversized", {0L}); + m_pConfig->addConfigValue("decoration:drop_shadow", {1L}); + m_pConfig->addConfigValue("decoration:shadow_range", {4L}); + m_pConfig->addConfigValue("decoration:shadow_render_power", {3L}); + m_pConfig->addConfigValue("decoration:shadow_ignore_window", {1L}); + m_pConfig->addConfigValue("decoration:shadow_offset", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("decoration:shadow_scale", {1.f}); + m_pConfig->addConfigValue("decoration:col.shadow", {0xee1a1a1aL}); + m_pConfig->addConfigValue("decoration:col.shadow_inactive", {(Hyprlang::INT)INT_MAX}); + m_pConfig->addConfigValue("decoration:dim_inactive", {0L}); + m_pConfig->addConfigValue("decoration:dim_strength", {0.5f}); + m_pConfig->addConfigValue("decoration:dim_special", {0.2f}); + m_pConfig->addConfigValue("decoration:dim_around", {0.4f}); + m_pConfig->addConfigValue("decoration:screen_shader", {STRVAL_EMPTY}); + + m_pConfig->addConfigValue("dwindle:pseudotile", {0L}); + m_pConfig->addConfigValue("dwindle:force_split", {0L}); + m_pConfig->addConfigValue("dwindle:permanent_direction_override", {0L}); + m_pConfig->addConfigValue("dwindle:preserve_split", {0L}); + m_pConfig->addConfigValue("dwindle:special_scale_factor", {1.f}); + m_pConfig->addConfigValue("dwindle:split_width_multiplier", {1.0f}); + m_pConfig->addConfigValue("dwindle:no_gaps_when_only", {0L}); + m_pConfig->addConfigValue("dwindle:use_active_for_splits", {1L}); + m_pConfig->addConfigValue("dwindle:default_split_ratio", {1.f}); + m_pConfig->addConfigValue("dwindle:smart_split", {0L}); + m_pConfig->addConfigValue("dwindle:smart_resizing", {1L}); + + m_pConfig->addConfigValue("master:special_scale_factor", {1.f}); + m_pConfig->addConfigValue("master:mfact", {0.55f}); + m_pConfig->addConfigValue("master:new_is_master", {1L}); + m_pConfig->addConfigValue("master:always_center_master", {0L}); + m_pConfig->addConfigValue("master:new_on_top", {0L}); + m_pConfig->addConfigValue("master:no_gaps_when_only", {0L}); + m_pConfig->addConfigValue("master:orientation", {"left"}); + m_pConfig->addConfigValue("master:inherit_fullscreen", {1L}); + m_pConfig->addConfigValue("master:allow_small_split", {0L}); + m_pConfig->addConfigValue("master:smart_resizing", {1L}); + m_pConfig->addConfigValue("master:drop_at_cursor", {1L}); + + m_pConfig->addConfigValue("animations:enabled", {1L}); + m_pConfig->addConfigValue("animations:first_launch_animation", {1L}); + + m_pConfig->addConfigValue("input:follow_mouse", {1L}); + m_pConfig->addConfigValue("input:mouse_refocus", {1L}); + m_pConfig->addConfigValue("input:special_fallthrough", {0L}); + m_pConfig->addConfigValue("input:sensitivity", {0.f}); + m_pConfig->addConfigValue("input:accel_profile", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_file", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_layout", {"us"}); + m_pConfig->addConfigValue("input:kb_variant", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_options", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_rules", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:kb_model", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:repeat_rate", {25L}); + m_pConfig->addConfigValue("input:repeat_delay", {600L}); + m_pConfig->addConfigValue("input:natural_scroll", {0L}); + m_pConfig->addConfigValue("input:numlock_by_default", {0L}); + m_pConfig->addConfigValue("input:resolve_binds_by_sym", {0L}); + m_pConfig->addConfigValue("input:force_no_accel", {0L}); + m_pConfig->addConfigValue("input:float_switch_override_focus", {1L}); + m_pConfig->addConfigValue("input:left_handed", {0L}); + m_pConfig->addConfigValue("input:scroll_method", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:scroll_button", {0L}); + m_pConfig->addConfigValue("input:scroll_button_lock", {0L}); + m_pConfig->addConfigValue("input:scroll_points", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:touchpad:natural_scroll", {0L}); + m_pConfig->addConfigValue("input:touchpad:disable_while_typing", {1L}); + m_pConfig->addConfigValue("input:touchpad:clickfinger_behavior", {0L}); + m_pConfig->addConfigValue("input:touchpad:tap_button_map", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:touchpad:middle_button_emulation", {0L}); + m_pConfig->addConfigValue("input:touchpad:tap-to-click", {1L}); + m_pConfig->addConfigValue("input:touchpad:tap-and-drag", {1L}); + m_pConfig->addConfigValue("input:touchpad:drag_lock", {0L}); + m_pConfig->addConfigValue("input:touchpad:scroll_factor", {1.f}); + m_pConfig->addConfigValue("input:touchdevice:transform", {0L}); + m_pConfig->addConfigValue("input:touchdevice:output", {"[[Auto]]"}); + m_pConfig->addConfigValue("input:touchdevice:enabled", {1L}); + m_pConfig->addConfigValue("input:tablet:transform", {0L}); + m_pConfig->addConfigValue("input:tablet:output", {STRVAL_EMPTY}); + m_pConfig->addConfigValue("input:tablet:region_position", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("input:tablet:region_size", Hyprlang::VEC2{0, 0}); + m_pConfig->addConfigValue("input:tablet:relative_input", {0L}); + + m_pConfig->addConfigValue("binds:pass_mouse_when_bound", {0L}); + m_pConfig->addConfigValue("binds:scroll_event_delay", {300L}); + m_pConfig->addConfigValue("binds:workspace_back_and_forth", {0L}); + m_pConfig->addConfigValue("binds:allow_workspace_cycles", {0L}); + m_pConfig->addConfigValue("binds:workspace_center_on", {1L}); + m_pConfig->addConfigValue("binds:focus_preferred_method", {0L}); + m_pConfig->addConfigValue("binds:ignore_group_lock", {0L}); + m_pConfig->addConfigValue("binds:movefocus_cycles_fullscreen", {1L}); + + m_pConfig->addConfigValue("gestures:workspace_swipe", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_fingers", {3L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_distance", {300L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_invert", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_min_speed_to_force", {30L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_cancel_ratio", {0.5f}); + m_pConfig->addConfigValue("gestures:workspace_swipe_create_new", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock", {1L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_direction_lock_threshold", {10L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_forever", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_numbered", {0L}); + m_pConfig->addConfigValue("gestures:workspace_swipe_use_r", {0L}); + + m_pConfig->addConfigValue("xwayland:use_nearest_neighbor", {1L}); + m_pConfig->addConfigValue("xwayland:force_zero_scaling", {0L}); + + m_pConfig->addConfigValue("opengl:nvidia_anti_flicker", {1L}); + m_pConfig->addConfigValue("opengl:force_introspection", {2L}); + + m_pConfig->addConfigValue("autogenerated", {0L}); + + m_pConfig->addConfigValue("general:col.active_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffffff"}); + m_pConfig->addConfigValue("general:col.inactive_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xff444444"}); + m_pConfig->addConfigValue("general:col.nogroup_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffaaff"}); + m_pConfig->addConfigValue("general:col.nogroup_border_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffff00ff"}); + + m_pConfig->addConfigValue("group:col.border_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ffff00"}); + m_pConfig->addConfigValue("group:col.border_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66777700"}); + m_pConfig->addConfigValue("group:col.border_locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); + m_pConfig->addConfigValue("group:col.border_locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); + + m_pConfig->addConfigValue("group:groupbar:col.active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ffff00"}); + m_pConfig->addConfigValue("group:groupbar:col.inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66777700"}); + m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); + m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); + + // devices + m_pConfig->addSpecialCategory("device", {"name"}); + m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); + m_pConfig->addSpecialConfigValue("device", "accel_profile", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_file", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_layout", {"us"}); + m_pConfig->addSpecialConfigValue("device", "kb_variant", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_options", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_rules", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_model", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "repeat_rate", {25L}); + m_pConfig->addSpecialConfigValue("device", "repeat_delay", {600L}); + m_pConfig->addSpecialConfigValue("device", "natural_scroll", {0L}); + m_pConfig->addSpecialConfigValue("device", "tap_button_map", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "numlock_by_default", {0L}); + m_pConfig->addSpecialConfigValue("device", "resolve_binds_by_sym", {0L}); + m_pConfig->addSpecialConfigValue("device", "disable_while_typing", {1L}); + m_pConfig->addSpecialConfigValue("device", "clickfinger_behavior", {0L}); + m_pConfig->addSpecialConfigValue("device", "middle_button_emulation", {0L}); + m_pConfig->addSpecialConfigValue("device", "tap-to-click", {1L}); + m_pConfig->addSpecialConfigValue("device", "tap-and-drag", {1L}); + m_pConfig->addSpecialConfigValue("device", "drag_lock", {0L}); + m_pConfig->addSpecialConfigValue("device", "left_handed", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_method", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "scroll_button", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_button_lock", {0L}); + m_pConfig->addSpecialConfigValue("device", "scroll_points", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "transform", {0L}); + m_pConfig->addSpecialConfigValue("device", "output", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "enabled", {1L}); // only for mice, touchpads, and touchdevices + m_pConfig->addSpecialConfigValue("device", "region_position", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "region_size", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "relative_input", {0L}); // only for tablets + + // keywords + m_pConfig->registerHandler(&::handleRawExec, "exec", {false}); + m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false}); + m_pConfig->registerHandler(&::handleMonitor, "monitor", {false}); + m_pConfig->registerHandler(&::handleBind, "bind", {true}); + m_pConfig->registerHandler(&::handleUnbind, "unbind", {false}); + m_pConfig->registerHandler(&::handleWorkspaceRules, "workspace", {false}); + m_pConfig->registerHandler(&::handleWindowRule, "windowrule", {false}); + m_pConfig->registerHandler(&::handleLayerRule, "layerrule", {false}); + m_pConfig->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false}); + m_pConfig->registerHandler(&::handleBezier, "bezier", {false}); + m_pConfig->registerHandler(&::handleAnimation, "animation", {false}); + m_pConfig->registerHandler(&::handleSource, "source", {false}); + m_pConfig->registerHandler(&::handleSubmap, "submap", {false}); + m_pConfig->registerHandler(&::handleBlurLS, "blurls", {false}); + m_pConfig->registerHandler(&::handlePlugin, "plugin", {false}); + m_pConfig->registerHandler(&::handleEnv, "env", {true}); + + // pluginza + m_pConfig->addSpecialCategory("plugin", {nullptr, true}); + + m_pConfig->commence(); Debug::log(LOG, "NOTE: further logs to stdout / logfile are disabled by default. Use debug:disable_logs and debug:enable_stdout_logs to override this."); - setDefaultVars(); setDefaultAnimationVars(); + resetHLConfig(); - configPaths.emplace_back(getMainConfigPath()); + Debug::disableLogs = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_logs")->getDataStaticPtr()); + Debug::disableTime = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_time")->getDataStaticPtr()); - Debug::disableLogs = &configValues["debug:disable_logs"].intValue; - Debug::disableTime = &configValues["debug:disable_time"].intValue; - - populateEnvironment(); + if (ERR.has_value()) + g_pHyprError->queueCreate(ERR.value(), CColor{1.0, 0.1, 0.1, 1.0}); } std::string CConfigManager::getConfigDir() { @@ -62,266 +604,13 @@ std::string CConfigManager::getMainConfigPath() { return getConfigDir() + "/hypr/" + (ISDEBUG ? "hyprlandd.conf" : "hyprland.conf"); } -void CConfigManager::populateEnvironment() { - environmentVariables.clear(); - for (char** env = environ; *env; ++env) { - const std::string ENVVAR = *env; - const auto VARIABLE = ENVVAR.substr(0, ENVVAR.find_first_of('=')); - const auto VALUE = ENVVAR.substr(ENVVAR.find_first_of('=') + 1); - environmentVariables.emplace_back(std::make_pair<>(VARIABLE, VALUE)); - } - - std::sort(environmentVariables.begin(), environmentVariables.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); }); -} - -void CConfigManager::setDefaultVars() { - configValues["general:max_fps"].intValue = 60; - configValues["general:sensitivity"].floatValue = 1.0f; - configValues["general:apply_sens_to_raw"].intValue = 0; - configValues["general:border_size"].intValue = 1; - configValues["general:no_border_on_floating"].intValue = 0; - configValues["general:border_part_of_window"].intValue = 1; - configValues["general:gaps_in"].intValue = 5; - configValues["general:gaps_out"].intValue = 20; - configValues["general:gaps_workspaces"].intValue = 0; - ((CGradientValueData*)configValues["general:col.active_border"].data.get())->reset(0xffffffff); - ((CGradientValueData*)configValues["general:col.inactive_border"].data.get())->reset(0xff444444); - ((CGradientValueData*)configValues["general:col.nogroup_border"].data.get())->reset(0xff444444); - ((CGradientValueData*)configValues["general:col.nogroup_border_active"].data.get())->reset(0xffff00ff); - configValues["general:cursor_inactive_timeout"].intValue = 0; - configValues["general:no_cursor_warps"].intValue = 0; - configValues["general:no_focus_fallback"].intValue = 0; - configValues["general:resize_on_border"].intValue = 0; - configValues["general:extend_border_grab_area"].intValue = 15; - configValues["general:hover_icon_on_border"].intValue = 1; - configValues["general:layout"].strValue = "dwindle"; - configValues["general:allow_tearing"].intValue = 0; - - configValues["misc:disable_hyprland_logo"].intValue = 0; - configValues["misc:disable_splash_rendering"].intValue = 0; - configValues["misc:force_hypr_chan"].intValue = 0; - configValues["misc:force_default_wallpaper"].intValue = -1; - configValues["misc:vfr"].intValue = 1; - configValues["misc:vrr"].intValue = 0; - configValues["misc:mouse_move_enables_dpms"].intValue = 0; - configValues["misc:key_press_enables_dpms"].intValue = 0; - configValues["misc:always_follow_on_dnd"].intValue = 1; - configValues["misc:layers_hog_keyboard_focus"].intValue = 1; - configValues["misc:animate_manual_resizes"].intValue = 0; - configValues["misc:animate_mouse_windowdragging"].intValue = 0; - configValues["misc:disable_autoreload"].intValue = 0; - configValues["misc:enable_swallow"].intValue = 0; - configValues["misc:swallow_regex"].strValue = STRVAL_EMPTY; - configValues["misc:swallow_exception_regex"].strValue = STRVAL_EMPTY; - configValues["misc:focus_on_activate"].intValue = 0; - configValues["misc:no_direct_scanout"].intValue = 1; - configValues["misc:hide_cursor_on_touch"].intValue = 1; - configValues["misc:mouse_move_focuses_monitor"].intValue = 1; - configValues["misc:render_ahead_of_time"].intValue = 0; - configValues["misc:render_ahead_safezone"].intValue = 1; - configValues["misc:cursor_zoom_factor"].floatValue = 1.f; - configValues["misc:cursor_zoom_rigid"].intValue = 0; - configValues["misc:allow_session_lock_restore"].intValue = 0; - configValues["misc:close_special_on_empty"].intValue = 1; - configValues["misc:background_color"].intValue = 0xff111111; - configValues["misc:new_window_takes_over_fullscreen"].intValue = 0; - - ((CGradientValueData*)configValues["group:col.border_active"].data.get())->reset(0x66ffff00); - ((CGradientValueData*)configValues["group:col.border_inactive"].data.get())->reset(0x66777700); - ((CGradientValueData*)configValues["group:col.border_locked_active"].data.get())->reset(0x66ff5500); - ((CGradientValueData*)configValues["group:col.border_locked_inactive"].data.get())->reset(0x66775500); - - configValues["group:insert_after_current"].intValue = 1; - configValues["group:focus_removed_window"].intValue = 1; - - configValues["group:groupbar:enabled"].intValue = 1; - configValues["group:groupbar:font_family"].strValue = "Sans"; - configValues["group:groupbar:font_size"].intValue = 8; - configValues["group:groupbar:gradients"].intValue = 1; - configValues["group:groupbar:priority"].intValue = 3; - configValues["group:groupbar:render_titles"].intValue = 1; - configValues["group:groupbar:scrolling"].intValue = 1; - configValues["group:groupbar:text_color"].intValue = 0xffffffff; - - ((CGradientValueData*)configValues["group:groupbar:col.active"].data.get())->reset(0x66ffff00); - ((CGradientValueData*)configValues["group:groupbar:col.inactive"].data.get())->reset(0x66777700); - ((CGradientValueData*)configValues["group:groupbar:col.locked_active"].data.get())->reset(0x66ff5500); - ((CGradientValueData*)configValues["group:groupbar:col.locked_inactive"].data.get())->reset(0x66775500); - - configValues["debug:int"].intValue = 0; - configValues["debug:log_damage"].intValue = 0; - configValues["debug:overlay"].intValue = 0; - configValues["debug:damage_blink"].intValue = 0; - configValues["debug:disable_logs"].intValue = 1; - configValues["debug:disable_time"].intValue = 1; - configValues["debug:enable_stdout_logs"].intValue = 0; - configValues["debug:damage_tracking"].intValue = DAMAGE_TRACKING_FULL; - configValues["debug:manual_crash"].intValue = 0; - configValues["debug:suppress_errors"].intValue = 0; - configValues["debug:watchdog_timeout"].intValue = 5; - configValues["debug:disable_scale_checks"].intValue = 0; - - configValues["decoration:rounding"].intValue = 0; - configValues["decoration:blur:enabled"].intValue = 1; - configValues["decoration:blur:size"].intValue = 8; - configValues["decoration:blur:passes"].intValue = 1; - configValues["decoration:blur:ignore_opacity"].intValue = 0; - configValues["decoration:blur:new_optimizations"].intValue = 1; - configValues["decoration:blur:xray"].intValue = 0; - configValues["decoration:blur:contrast"].floatValue = 0.8916; - configValues["decoration:blur:brightness"].floatValue = 1.0; - configValues["decoration:blur:vibrancy"].floatValue = 0.1696; - configValues["decoration:blur:vibrancy_darkness"].floatValue = 0.0; - configValues["decoration:blur:noise"].floatValue = 0.0117; - configValues["decoration:blur:special"].intValue = 0; - configValues["decoration:blur:popups"].intValue = 0; - configValues["decoration:blur:popups_ignorealpha"].floatValue = 0.2; - configValues["decoration:active_opacity"].floatValue = 1; - configValues["decoration:inactive_opacity"].floatValue = 1; - configValues["decoration:fullscreen_opacity"].floatValue = 1; - configValues["decoration:no_blur_on_oversized"].intValue = 0; - configValues["decoration:drop_shadow"].intValue = 1; - configValues["decoration:shadow_range"].intValue = 4; - configValues["decoration:shadow_render_power"].intValue = 3; - configValues["decoration:shadow_ignore_window"].intValue = 1; - configValues["decoration:shadow_offset"].vecValue = Vector2D(); - configValues["decoration:shadow_scale"].floatValue = 1.f; - configValues["decoration:col.shadow"].intValue = 0xee1a1a1a; - configValues["decoration:col.shadow_inactive"].intValue = INT_MAX; - configValues["decoration:dim_inactive"].intValue = 0; - configValues["decoration:dim_strength"].floatValue = 0.5f; - configValues["decoration:dim_special"].floatValue = 0.2f; - configValues["decoration:dim_around"].floatValue = 0.4f; - configValues["decoration:screen_shader"].strValue = STRVAL_EMPTY; - - configValues["dwindle:pseudotile"].intValue = 0; - configValues["dwindle:force_split"].intValue = 0; - configValues["dwindle:permanent_direction_override"].intValue = 0; - configValues["dwindle:preserve_split"].intValue = 0; - configValues["dwindle:special_scale_factor"].floatValue = 1.f; - configValues["dwindle:split_width_multiplier"].floatValue = 1.0f; - configValues["dwindle:no_gaps_when_only"].intValue = 0; - configValues["dwindle:use_active_for_splits"].intValue = 1; - configValues["dwindle:default_split_ratio"].floatValue = 1.f; - configValues["dwindle:smart_split"].intValue = 0; - configValues["dwindle:smart_resizing"].intValue = 1; - - configValues["master:special_scale_factor"].floatValue = 1.f; - configValues["master:mfact"].floatValue = 0.55f; - configValues["master:new_is_master"].intValue = 1; - configValues["master:always_center_master"].intValue = 0; - configValues["master:new_on_top"].intValue = 0; - configValues["master:no_gaps_when_only"].intValue = 0; - configValues["master:orientation"].strValue = "left"; - configValues["master:inherit_fullscreen"].intValue = 1; - configValues["master:allow_small_split"].intValue = 0; - configValues["master:smart_resizing"].intValue = 1; - configValues["master:drop_at_cursor"].intValue = 1; - - configValues["animations:enabled"].intValue = 1; - configValues["animations:first_launch_animation"].intValue = 1; - - configValues["input:follow_mouse"].intValue = 1; - configValues["input:mouse_refocus"].intValue = 1; - configValues["input:sensitivity"].floatValue = 0.f; - configValues["input:accel_profile"].strValue = STRVAL_EMPTY; - configValues["input:kb_file"].strValue = STRVAL_EMPTY; - configValues["input:kb_layout"].strValue = "us"; - configValues["input:kb_variant"].strValue = STRVAL_EMPTY; - configValues["input:kb_options"].strValue = STRVAL_EMPTY; - configValues["input:kb_rules"].strValue = STRVAL_EMPTY; - configValues["input:kb_model"].strValue = STRVAL_EMPTY; - configValues["input:repeat_rate"].intValue = 25; - configValues["input:repeat_delay"].intValue = 600; - configValues["input:natural_scroll"].intValue = 0; - configValues["input:numlock_by_default"].intValue = 0; - configValues["input:force_no_accel"].intValue = 0; - configValues["input:float_switch_override_focus"].intValue = 1; - configValues["input:left_handed"].intValue = 0; - configValues["input:scroll_method"].strValue = STRVAL_EMPTY; - configValues["input:scroll_button"].intValue = 0; - configValues["input:scroll_button_lock"].intValue = 0; - configValues["input:scroll_points"].strValue = STRVAL_EMPTY; - configValues["input:touchpad:natural_scroll"].intValue = 0; - configValues["input:touchpad:disable_while_typing"].intValue = 1; - configValues["input:touchpad:clickfinger_behavior"].intValue = 0; - configValues["input:touchpad:tap_button_map"].strValue = STRVAL_EMPTY; - configValues["input:touchpad:middle_button_emulation"].intValue = 0; - configValues["input:touchpad:tap-to-click"].intValue = 1; - configValues["input:touchpad:tap-and-drag"].intValue = 1; - configValues["input:touchpad:drag_lock"].intValue = 0; - configValues["input:touchpad:scroll_factor"].floatValue = 1.f; - configValues["input:touchdevice:transform"].intValue = 0; - configValues["input:touchdevice:output"].strValue = STRVAL_EMPTY; - configValues["input:tablet:transform"].intValue = 0; - configValues["input:tablet:output"].strValue = STRVAL_EMPTY; - configValues["input:tablet:region_position"].vecValue = Vector2D(); - configValues["input:tablet:region_size"].vecValue = Vector2D(); - configValues["input:tablet:relative_input"].intValue = 0; - - configValues["binds:pass_mouse_when_bound"].intValue = 0; - configValues["binds:scroll_event_delay"].intValue = 300; - configValues["binds:workspace_back_and_forth"].intValue = 0; - configValues["binds:allow_workspace_cycles"].intValue = 0; - configValues["binds:workspace_center_on"].intValue = 1; - configValues["binds:focus_preferred_method"].intValue = 0; - configValues["binds:ignore_group_lock"].intValue = 0; - configValues["binds:movefocus_cycles_fullscreen"].intValue = 1; - - configValues["gestures:workspace_swipe"].intValue = 0; - configValues["gestures:workspace_swipe_fingers"].intValue = 3; - configValues["gestures:workspace_swipe_distance"].intValue = 300; - configValues["gestures:workspace_swipe_invert"].intValue = 1; - configValues["gestures:workspace_swipe_min_speed_to_force"].intValue = 30; - configValues["gestures:workspace_swipe_cancel_ratio"].floatValue = 0.5f; - configValues["gestures:workspace_swipe_create_new"].intValue = 1; - configValues["gestures:workspace_swipe_direction_lock"].intValue = 1; - configValues["gestures:workspace_swipe_direction_lock_threshold"].intValue = 10; - configValues["gestures:workspace_swipe_forever"].intValue = 0; - configValues["gestures:workspace_swipe_numbered"].intValue = 0; - configValues["gestures:workspace_swipe_use_r"].intValue = 0; - - configValues["xwayland:use_nearest_neighbor"].intValue = 1; - configValues["xwayland:force_zero_scaling"].intValue = 0; - - configValues["opengl:nvidia_anti_flicker"].intValue = 1; - - configValues["autogenerated"].intValue = 0; -} - -void CConfigManager::setDeviceDefaultVars(const std::string& dev) { - auto& cfgValues = deviceConfigs[dev]; - - cfgValues["sensitivity"].floatValue = 0.f; - cfgValues["accel_profile"].strValue = STRVAL_EMPTY; - cfgValues["kb_file"].strValue = STRVAL_EMPTY; - cfgValues["kb_layout"].strValue = "us"; - cfgValues["kb_variant"].strValue = STRVAL_EMPTY; - cfgValues["kb_options"].strValue = STRVAL_EMPTY; - cfgValues["kb_rules"].strValue = STRVAL_EMPTY; - cfgValues["kb_model"].strValue = STRVAL_EMPTY; - cfgValues["repeat_rate"].intValue = 25; - cfgValues["repeat_delay"].intValue = 600; - cfgValues["natural_scroll"].intValue = 0; - cfgValues["tap_button_map"].strValue = STRVAL_EMPTY; - cfgValues["numlock_by_default"].intValue = 0; - cfgValues["disable_while_typing"].intValue = 1; - cfgValues["clickfinger_behavior"].intValue = 0; - cfgValues["middle_button_emulation"].intValue = 0; - cfgValues["tap-to-click"].intValue = 1; - cfgValues["tap-and-drag"].intValue = 1; - cfgValues["drag_lock"].intValue = 0; - cfgValues["left_handed"].intValue = 0; - cfgValues["scroll_method"].strValue = STRVAL_EMPTY; - cfgValues["scroll_button"].intValue = 0; - cfgValues["scroll_button_lock"].intValue = 0; - cfgValues["scroll_points"].strValue = STRVAL_EMPTY; - cfgValues["transform"].intValue = 0; - cfgValues["output"].strValue = STRVAL_EMPTY; - cfgValues["enabled"].intValue = 1; // only for mice / touchpads - cfgValues["region_position"].vecValue = Vector2D(); // only for tablets - cfgValues["region_size"].vecValue = Vector2D(); // only for tablets - cfgValues["relative_input"].intValue = 0; // only for tablets +void CConfigManager::reload() { + EMIT_HOOK_EVENT("preConfigReload", nullptr); + setDefaultAnimationVars(); + resetHLConfig(); + configCurrentPath = getMainConfigPath(); + const auto ERR = m_pConfig->parse(); + postConfigReload(ERR); } void CConfigManager::setDefaultAnimationVars() { @@ -373,1276 +662,38 @@ void CConfigManager::setDefaultAnimationVars() { CREATEANIMCFG("specialWorkspace", "workspaces"); } -void CConfigManager::init() { +std::optional CConfigManager::verifyConfigExists() { + std::string mainConfigPath = getMainConfigPath(); - loadConfigLoadVars(); + if (g_pCompositor->explicitConfigPath.empty() && !std::filesystem::exists(mainConfigPath)) { + std::string configPath = std::filesystem::path(mainConfigPath).parent_path(); - const std::string CONFIGPATH = getMainConfigPath(); + if (!std::filesystem::is_directory(configPath)) { + Debug::log(WARN, "Creating config home directory"); + try { + std::filesystem::create_directories(configPath); + } catch (...) { return "Broken config file! (Could not create config directory)"; } + } - struct stat fileStat; - int err = stat(CONFIGPATH.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at statting config, error {}", errno); + Debug::log(WARN, "No config file found; attempting to generate."); + std::ofstream ofs; + ofs.open(mainConfigPath, std::ios::trunc); + ofs << AUTOCONFIG; + ofs.close(); } - configModifyTimes[CONFIGPATH] = fileStat.st_mtime; + if (!std::filesystem::exists(mainConfigPath)) + return "broken config dir?"; - isFirstLaunch = false; + return {}; } -void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) { - if (!configValues.contains(COMMAND)) { - if (!COMMAND.starts_with("device:") /* devices parsed later */ && !COMMAND.starts_with("plugin:") /* plugins parsed later */) { - if (COMMAND[0] == '$') { - // register a dynamic var - Debug::log(LOG, "Registered dynamic var \"{}\" -> {}", COMMAND, VALUE); - configDynamicVars.emplace_back(std::make_pair<>(COMMAND.substr(1), VALUE)); - - std::sort(configDynamicVars.begin(), configDynamicVars.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); }); - } else { - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; - } - - return; - } - } - - SConfigValue* CONFIGENTRY = nullptr; - - if (COMMAND.starts_with("device:")) { - const auto DEVICE = COMMAND.substr(7).substr(0, COMMAND.find_last_of(':') - 7); - const auto CONFIGVAR = COMMAND.substr(COMMAND.find_last_of(':') + 1); - - if (!deviceConfigExists(DEVICE)) - setDeviceDefaultVars(DEVICE); - - auto it = deviceConfigs.find(DEVICE); - - if (it->second.find(CONFIGVAR) == it->second.end()) { - if (it->second.contains("touch_output") || it->second.contains("touch_transform")) { - parseError = "touch_output and touch_transform have been changed to output and transform respectively"; - return; - } - - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; - return; - } - - CONFIGENTRY = &it->second.at(CONFIGVAR); - } else if (COMMAND.starts_with("plugin:")) { - for (auto& [handle, pMap] : pluginConfigs) { - auto it = std::find_if(pMap->begin(), pMap->end(), [&](const auto& other) { return other.first == COMMAND; }); - if (it == pMap->end()) { - continue; // May be in another plugin - } - - CONFIGENTRY = &it->second; - } - - if (!CONFIGENTRY) { - m_vFailedPluginConfigValues.emplace_back(std::make_pair<>(COMMAND, VALUE)); - return; // silent ignore - } - } else { - CONFIGENTRY = &configValues.at(COMMAND); - } - - CONFIGENTRY->set = true; - - if (CONFIGENTRY->intValue != -INT64_MAX) { - try { - CONFIGENTRY->intValue = configStringToInt(VALUE); - } catch (std::exception& e) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. " + e.what(); - } - } else if (CONFIGENTRY->floatValue != -__FLT_MAX__) { - try { - CONFIGENTRY->floatValue = stof(VALUE); - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->strValue != "") { - try { - CONFIGENTRY->strValue = VALUE; - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->vecValue != Vector2D(-__FLT_MAX__, -__FLT_MAX__)) { - try { - if (const auto SPACEPOS = VALUE.find(' '); SPACEPOS != std::string::npos) { - const auto X = VALUE.substr(0, SPACEPOS); - const auto Y = VALUE.substr(SPACEPOS + 1); - - if (isNumber(X, true) && isNumber(Y, true)) { - CONFIGENTRY->vecValue = Vector2D(std::stof(X), std::stof(Y)); - } - } else { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - } else if (CONFIGENTRY->data.get() != nullptr) { - - switch (CONFIGENTRY->data->getDataType()) { - case CVD_TYPE_GRADIENT: { - - CVarList varlist(VALUE, 0, ' '); - - CGradientValueData* data = (CGradientValueData*)CONFIGENTRY->data.get(); - data->m_vColors.clear(); - - for (auto& var : varlist) { - if (var.find("deg") != std::string::npos) { - // last arg - try { - data->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians - } catch (...) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; - } - - break; - } - - if (data->m_vColors.size() >= 10) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. Max colors in a gradient is 10."; - break; - } - - try { - data->m_vColors.push_back(CColor(configStringToInt(var))); - } catch (std::exception& e) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. " + e.what(); - } - } - - if (data->m_vColors.size() == 0) { - Debug::log(WARN, "Error reading value of {}", COMMAND); - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. No colors provided."; - - data->m_vColors.push_back(0); // transparent - } - - break; - } - default: { - UNREACHABLE(); - } - } - } - - if (COMMAND == "decoration:screen_shader") { - const auto PATH = absolutePath(VALUE, configCurrentPath); - - configPaths.push_back(PATH); - - struct stat fileStat; - int err = stat(PATH.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at {}, error {}: {}", PATH, err, strerror(err)); - return; - } - - configModifyTimes[PATH] = fileStat.st_mtime; - } -} - -void CConfigManager::handleRawExec(const std::string& command, const std::string& args) { - // Exec in the background dont wait for it. - g_pKeybindManager->spawn(args); -} - -static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { - auto args = CVarList(modeline, 0, 's'); - - auto keyword = args[0]; - std::transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower); - - if (keyword != "modeline") - return false; - - if (args.size() < 10) { - Debug::log(ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); - return false; - } - - int argno = 1; - - mode.type = DRM_MODE_TYPE_USERDEF; - mode.clock = std::stof(args[argno++]) * 1000; - mode.hdisplay = std::stoi(args[argno++]); - mode.hsync_start = std::stoi(args[argno++]); - mode.hsync_end = std::stoi(args[argno++]); - mode.htotal = std::stoi(args[argno++]); - mode.vdisplay = std::stoi(args[argno++]); - mode.vsync_start = std::stoi(args[argno++]); - mode.vsync_end = std::stoi(args[argno++]); - mode.vtotal = std::stoi(args[argno++]); - mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; - - static std::unordered_map flagsmap = { - {"+hsync", DRM_MODE_FLAG_PHSYNC}, - {"-hsync", DRM_MODE_FLAG_NHSYNC}, - {"+vsync", DRM_MODE_FLAG_PVSYNC}, - {"-vsync", DRM_MODE_FLAG_NVSYNC}, - }; - - for (; argno < static_cast(args.size()); argno++) { - auto key = args[argno]; - std::transform(key.begin(), key.end(), key.begin(), ::tolower); - - auto it = flagsmap.find(key); - - if (it != flagsmap.end()) - mode.flags |= it->second; - else - Debug::log(ERR, "invalid flag {} in modeline", it->first); - } - - snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); - - return true; -} - -void CConfigManager::handleMonitor(const std::string& command, const std::string& args) { - - // get the monitor config - SMonitorRule newrule; - - const auto ARGS = CVarList(args); - - newrule.name = ARGS[0]; - - if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") { - if (ARGS[1] == "disable" || ARGS[1] == "disabled") - newrule.disabled = true; - else if (ARGS[1] == "transform") { - const auto TSF = std::stoi(ARGS[2]); - if (std::clamp(TSF, 0, 7) != TSF) { - Debug::log(ERR, "invalid transform {} in monitor", TSF); - parseError = "invalid transform"; - return; - } - - const auto TRANSFORM = (wl_output_transform)TSF; - - // overwrite if exists - for (auto& r : m_dMonitorRules) { - if (r.name == newrule.name) { - r.transform = TRANSFORM; - return; - } - } - - return; - } else if (ARGS[1] == "addreserved") { - int top = std::stoi(ARGS[2]); - - int bottom = std::stoi(ARGS[3]); - - int left = std::stoi(ARGS[4]); - - int right = std::stoi(ARGS[5]); - - m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right}; - - return; // this is not a rule, ignore - } else { - Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); - return; - } - - std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); - - m_dMonitorRules.push_back(newrule); - - return; - } - - if (ARGS[1].starts_with("pref")) { - newrule.resolution = Vector2D(); - } else if (ARGS[1].starts_with("highrr")) { - newrule.resolution = Vector2D(-1, -1); - } else if (ARGS[1].starts_with("highres")) { - newrule.resolution = Vector2D(-1, -2); - } else if (parseModeLine(ARGS[1], newrule.drmMode)) { - newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); - newrule.refreshRate = newrule.drmMode.vrefresh / 1000; - } else { - newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x'))); - newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@'))); - - if (ARGS[1].contains("@")) - newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1)); - } - - if (ARGS[2].starts_with("auto")) { - newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); - } else { - newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x'))); - newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1)); - } - - if (ARGS[3].starts_with("auto")) { - newrule.scale = -1; - } else { - newrule.scale = stof(ARGS[3]); - - if (newrule.scale < 0.25f) { - parseError = "not a valid scale."; - newrule.scale = 1; - } - } - - int argno = 4; - - while (ARGS[argno] != "") { - if (ARGS[argno] == "mirror") { - newrule.mirrorOf = ARGS[argno + 1]; - argno++; - } else if (ARGS[argno] == "bitdepth") { - newrule.enable10bit = ARGS[argno + 1] == "10"; - argno++; - } else if (ARGS[argno] == "transform") { - newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]); - argno++; - } else if (ARGS[argno] == "vrr") { - newrule.vrr = std::stoi(ARGS[argno + 1]); - argno++; - } else if (ARGS[argno] == "workspace") { - std::string name = ""; - int wsId = getWorkspaceIDFromString(ARGS[argno + 1], name); - - SWorkspaceRule wsRule; - wsRule.monitor = newrule.name; - wsRule.workspaceString = ARGS[argno + 1]; - wsRule.workspaceName = name; - wsRule.workspaceId = wsId; - - m_dWorkspaceRules.emplace_back(wsRule); - argno++; - } else { - Debug::log(ERR, "Config error: invalid monitor syntax"); - parseError = "invalid syntax at \"" + ARGS[argno] + "\""; - return; - } - - argno++; - } - - std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); - - m_dMonitorRules.push_back(newrule); -} - -void CConfigManager::handleBezier(const std::string& command, const std::string& args) { - const auto ARGS = CVarList(args); - - std::string bezierName = ARGS[0]; - - if (ARGS[1] == "") - parseError = "too few arguments"; - float p1x = std::stof(ARGS[1]); - - if (ARGS[2] == "") - parseError = "too few arguments"; - float p1y = std::stof(ARGS[2]); - - if (ARGS[3] == "") - parseError = "too few arguments"; - float p2x = std::stof(ARGS[3]); - - if (ARGS[4] == "") - parseError = "too few arguments"; - float p2y = std::stof(ARGS[4]); - - if (ARGS[5] != "") - parseError = "too many arguments"; - - g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y)); -} - -void CConfigManager::setAnimForChildren(SAnimationPropertyConfig* const ANIM) { - for (auto& [name, anim] : animationConfig) { - if (anim.pParentAnimation == ANIM && !anim.overridden) { - // if a child isnt overridden, set the values of the parent - anim.pValues = ANIM->pValues; - - setAnimForChildren(&anim); - } - } -}; - -void CConfigManager::handleAnimation(const std::string& command, const std::string& args) { - const auto ARGS = CVarList(args); - - // Master on/off - - // anim name - const auto ANIMNAME = ARGS[0]; - - const auto PANIM = animationConfig.find(ANIMNAME); - - if (PANIM == animationConfig.end()) { - parseError = "no such animation"; - return; - } - - PANIM->second.overridden = true; - PANIM->second.pValues = &PANIM->second; - - // on/off - PANIM->second.internalEnabled = ARGS[1] == "1"; - - if (ARGS[1] != "0" && ARGS[1] != "1") { - parseError = "invalid animation on/off state"; - } - - if (PANIM->second.internalEnabled) { - // speed - if (isNumber(ARGS[2], true)) { - PANIM->second.internalSpeed = std::stof(ARGS[2]); - - if (PANIM->second.internalSpeed <= 0) { - parseError = "invalid speed"; - PANIM->second.internalSpeed = 1.f; - } - } else { - PANIM->second.internalSpeed = 10.f; - parseError = "invalid speed"; - } - - // curve - PANIM->second.internalBezier = ARGS[3]; - - if (!g_pAnimationManager->bezierExists(ARGS[3])) { - parseError = "no such bezier"; - PANIM->second.internalBezier = "default"; - } - - // style - PANIM->second.internalStyle = ARGS[4]; - - if (ARGS[4] != "") { - const auto ERR = g_pAnimationManager->styleValidInConfigVar(ANIMNAME, ARGS[4]); - - if (ERR != "") - parseError = ERR; - } - } - - // now, check for children, recursively - setAnimForChildren(&PANIM->second); -} - -void CConfigManager::handleBind(const std::string& command, const std::string& value) { - // example: - // bind[fl]=SUPER,G,exec,dmenu_run - - // flags - bool locked = false; - bool release = false; - bool repeat = false; - bool mouse = false; - bool nonConsuming = false; - bool transparent = false; - bool ignoreMods = false; - const auto BINDARGS = command.substr(4); - - for (auto& arg : BINDARGS) { - if (arg == 'l') { - locked = true; - } else if (arg == 'r') { - release = true; - } else if (arg == 'e') { - repeat = true; - } else if (arg == 'm') { - mouse = true; - } else if (arg == 'n') { - nonConsuming = true; - } else if (arg == 't') { - transparent = true; - } else if (arg == 'i') { - ignoreMods = true; - } else { - parseError = "bind: invalid flag"; - return; - } - } - - if (release && repeat) { - parseError = "flags r and e are mutually exclusive"; - return; - } - - if (mouse && (repeat || release || locked)) { - parseError = "flag m is exclusive"; - return; - } - - const auto ARGS = CVarList(value, 4); - - if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse)) { - parseError = "bind: too few args"; - return; - } else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse)) { - parseError = "bind: too many args"; - return; - } - - const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); - const auto MODSTR = ARGS[0]; - - const auto KEY = ARGS[1]; - - auto HANDLER = ARGS[2]; - - const auto COMMAND = mouse ? HANDLER : ARGS[3]; - - if (mouse) - HANDLER = "mouse"; - - // to lower - std::transform(HANDLER.begin(), HANDLER.end(), HANDLER.begin(), ::tolower); - - const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(HANDLER); - - if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) { - Debug::log(ERR, "Invalid dispatcher!"); - parseError = "Invalid dispatcher, requested \"" + HANDLER + "\" does not exist"; - return; - } - - if (MOD == 0 && MODSTR != "") { - Debug::log(ERR, "Invalid mod!"); - parseError = "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; - return; - } - - if (KEY != "") { - if (isNumber(KEY) && std::stoi(KEY) > 9) - g_pKeybindManager->addKeybind( - SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); - else if (KEY.starts_with("code:") && isNumber(KEY.substr(5))) - g_pKeybindManager->addKeybind( - SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); - else - g_pKeybindManager->addKeybind(SKeybind{KEY, -1, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); - } -} - -void CConfigManager::handleUnbind(const std::string& command, const std::string& value) { - const auto ARGS = CVarList(value); - - const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); - - const auto KEY = ARGS[1]; - - g_pKeybindManager->removeKeybind(MOD, KEY); -} - -bool windowRuleValid(const std::string& RULE) { - return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || - RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || - RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "nofullscreenrequest" || - RULE == "nomaximizerequest" || RULE == "fakefullscreen" || RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || - RULE == "maximize" || RULE == "keepaspectratio" || RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || - RULE.starts_with("bordercolor") || RULE == "forcergbx" || RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || - RULE.starts_with("center") || RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor"; -} - -bool layerRuleValid(const std::string& RULE) { - return RULE == "noanim" || RULE == "blur" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE.starts_with("xray"); -} - -void CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - - // check rule and value - if (RULE == "" || VALUE == "") { - return; - } - - if (RULE == "unset") { - std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { return other.szValue == VALUE; }); - return; - } - - // verify we support a rule - if (!windowRuleValid(RULE)) { - Debug::log(ERR, "Invalid rule found: {}", RULE); - parseError = "Invalid rule found: " + RULE; - return; - } - - if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) - m_dWindowRules.push_front({RULE, VALUE}); - else - m_dWindowRules.push_back({RULE, VALUE}); -} - -void CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); - - // check rule and value - if (RULE == "" || VALUE == "") - return; - - if (RULE == "unset") { - std::erase_if(m_dLayerRules, [&](const SLayerRule& other) { return other.targetNamespace == VALUE; }); - return; - } - - if (!layerRuleValid(RULE)) { - Debug::log(ERR, "Invalid rule found: {}", RULE); - parseError = "Invalid rule found: " + RULE; - return; - } - - m_dLayerRules.push_back({VALUE, RULE}); - - for (auto& m : g_pCompositor->m_vMonitors) - for (auto& lsl : m->m_aLayerSurfaceLayers) - for (auto& ls : lsl) - ls->applyRules(); -} - -void CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) { - const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); - const auto VALUE = value.substr(value.find_first_of(',') + 1); - - if (!windowRuleValid(RULE) && RULE != "unset") { - Debug::log(ERR, "Invalid rulev2 found: {}", RULE); - parseError = "Invalid rulev2 found: " + RULE; - return; - } - - // now we estract shit from the value - SWindowRule rule; - rule.v2 = true; - rule.szRule = RULE; - rule.szValue = VALUE; - - const auto TITLEPOS = VALUE.find("title:"); - const auto CLASSPOS = VALUE.find("class:"); - const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); - const auto INITIALCLASSPOS = VALUE.find("initialClass:"); - const auto X11POS = VALUE.find("xwayland:"); - const auto FLOATPOS = VALUE.find("floating:"); - const auto FULLSCREENPOS = VALUE.find("fullscreen:"); - const auto PINNEDPOS = VALUE.find("pinned:"); - const auto FOCUSPOS = VALUE.find("focus:"); - const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); - - // find workspacepos that isn't onworkspacepos - size_t WORKSPACEPOS = std::string::npos; - size_t currentPos = VALUE.find("workspace:"); - while (currentPos != std::string::npos) { - if (currentPos == 0 || VALUE[currentPos - 1] != 'n') { - WORKSPACEPOS = currentPos; - break; - } - currentPos = VALUE.find("workspace:", currentPos + 1); - } - - if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && - X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && - FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { - Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); - parseError = "Invalid rulev2 syntax: " + VALUE; - return; - } - - auto extract = [&](size_t pos) -> std::string { - std::string result; - result = VALUE.substr(pos); - - size_t min = 999999; - if (TITLEPOS > pos && TITLEPOS < min) - min = TITLEPOS; - if (CLASSPOS > pos && CLASSPOS < min) - min = CLASSPOS; - if (INITIALTITLEPOS > pos && INITIALTITLEPOS < min) - min = INITIALTITLEPOS; - if (INITIALCLASSPOS > pos && INITIALCLASSPOS < min) - min = INITIALCLASSPOS; - if (X11POS > pos && X11POS < min) - min = X11POS; - if (FLOATPOS > pos && FLOATPOS < min) - min = FLOATPOS; - if (FULLSCREENPOS > pos && FULLSCREENPOS < min) - min = FULLSCREENPOS; - if (PINNEDPOS > pos && PINNEDPOS < min) - min = PINNEDPOS; - if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) - min = ONWORKSPACEPOS; - if (WORKSPACEPOS > pos && WORKSPACEPOS < min) - min = WORKSPACEPOS; - if (FOCUSPOS > pos && FOCUSPOS < min) - min = FOCUSPOS; - - result = result.substr(0, min - pos); - - result = removeBeginEndSpacesTabs(result); - - if (result.back() == ',') - result.pop_back(); - - return result; - }; - - if (CLASSPOS != std::string::npos) - rule.szClass = extract(CLASSPOS + 6); - - if (TITLEPOS != std::string::npos) - rule.szTitle = extract(TITLEPOS + 6); - - if (INITIALCLASSPOS != std::string::npos) - rule.szInitialClass = extract(INITIALCLASSPOS + 13); - - if (INITIALTITLEPOS != std::string::npos) - rule.szInitialTitle = extract(INITIALTITLEPOS + 13); - - if (X11POS != std::string::npos) - rule.bX11 = extract(X11POS + 9) == "1" ? 1 : 0; - - if (FLOATPOS != std::string::npos) - rule.bFloating = extract(FLOATPOS + 9) == "1" ? 1 : 0; - - if (FULLSCREENPOS != std::string::npos) - rule.bFullscreen = extract(FULLSCREENPOS + 11) == "1" ? 1 : 0; - - if (PINNEDPOS != std::string::npos) - rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; - - if (WORKSPACEPOS != std::string::npos) - rule.szWorkspace = extract(WORKSPACEPOS + 10); - - if (FOCUSPOS != std::string::npos) - rule.bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; - - if (ONWORKSPACEPOS != std::string::npos) - rule.iOnWorkspace = configStringToInt(extract(ONWORKSPACEPOS + 12)); - - if (RULE == "unset") { - std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { - if (!other.v2) { - return other.szClass == rule.szClass && !rule.szClass.empty(); - } else { - if (!rule.szClass.empty() && rule.szClass != other.szClass) - return false; - - if (!rule.szTitle.empty() && rule.szTitle != other.szTitle) - return false; - - if (!rule.szInitialClass.empty() && rule.szInitialClass != other.szInitialClass) - return false; - - if (!rule.szInitialTitle.empty() && rule.szInitialTitle != other.szInitialTitle) - return false; - - if (rule.bX11 != -1 && rule.bX11 != other.bX11) - return false; - - if (rule.bFloating != -1 && rule.bFloating != other.bFloating) - return false; - - if (rule.bFullscreen != -1 && rule.bFullscreen != other.bFullscreen) - return false; - - if (rule.bPinned != -1 && rule.bPinned != other.bPinned) - return false; - - if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace) - return false; - - if (rule.bFocus != -1 && rule.bFocus != other.bFocus) - return false; - - if (rule.iOnWorkspace != -1 && rule.iOnWorkspace != other.iOnWorkspace) - return false; - - return true; - } - }); - return; - } - - if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) - m_dWindowRules.push_front(rule); - else - m_dWindowRules.push_back(rule); -} - -void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { - const bool BYADDRESS = name.starts_with("address:"); - std::string matchName = name; - - if (BYADDRESS) { - matchName = matchName.substr(8); - } - - for (auto& m : g_pCompositor->m_vMonitors) { - for (auto& lsl : m->m_aLayerSurfaceLayers) { - for (auto& ls : lsl) { - if (BYADDRESS) { - if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) - ls->forceBlur = forceBlur; - } else if (ls->szNamespace == matchName) - ls->forceBlur = forceBlur; - } - } - } -} - -void CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { - if (value.starts_with("remove,")) { - const auto TOREMOVE = removeBeginEndSpacesTabs(value.substr(7)); - if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) - updateBlurredLS(TOREMOVE, false); - return; - } - - m_dBlurLSNamespaces.emplace_back(value); - updateBlurredLS(value, true); -} - -void CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { - // This can either be the monitor or the workspace identifier - const auto FIRST_DELIM = value.find_first_of(','); - - std::string name = ""; - auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM)); - int id = getWorkspaceIDFromString(first_ident, name); - - auto rules = value.substr(FIRST_DELIM + 1); - SWorkspaceRule wsRule; - wsRule.workspaceString = first_ident; - if (id == WORKSPACE_INVALID) { - // it could be the monitor. If so, second value MUST be - // the workspace. - const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); - auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); - id = getWorkspaceIDFromString(wsIdent, name); - if (id == WORKSPACE_INVALID) { - Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); - parseError = "Invalid workspace identifier found: " + wsIdent; - return; - } - wsRule.monitor = first_ident; - wsRule.workspaceString = wsIdent; - wsRule.isDefault = true; // backwards compat - rules = value.substr(WORKSPACE_DELIM + 1); - } - - const static std::string ruleOnCreatedEmtpy = "on-created-empty:"; - const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length(); - - auto assignRule = [&](std::string rule) { - size_t delim = std::string::npos; - if ((delim = rule.find("gapsin:")) != std::string::npos) - wsRule.gapsIn = std::stoi(rule.substr(delim + 7)); - else if ((delim = rule.find("gapsout:")) != std::string::npos) - wsRule.gapsOut = std::stoi(rule.substr(delim + 8)); - else if ((delim = rule.find("bordersize:")) != std::string::npos) - wsRule.borderSize = std::stoi(rule.substr(delim + 11)); - else if ((delim = rule.find("border:")) != std::string::npos) - wsRule.border = configStringToInt(rule.substr(delim + 7)); - else if ((delim = rule.find("shadow:")) != std::string::npos) - wsRule.shadow = configStringToInt(rule.substr(delim + 7)); - else if ((delim = rule.find("rounding:")) != std::string::npos) - wsRule.rounding = configStringToInt(rule.substr(delim + 9)); - else if ((delim = rule.find("decorate:")) != std::string::npos) - wsRule.decorate = configStringToInt(rule.substr(delim + 9)); - else if ((delim = rule.find("monitor:")) != std::string::npos) - wsRule.monitor = rule.substr(delim + 8); - else if ((delim = rule.find("default:")) != std::string::npos) - wsRule.isDefault = configStringToInt(rule.substr(delim + 8)); - else if ((delim = rule.find("persistent:")) != std::string::npos) - wsRule.isPersistent = configStringToInt(rule.substr(delim + 11)); - else if ((delim = rule.find(ruleOnCreatedEmtpy)) != std::string::npos) - wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmtpyLen)); - else if ((delim = rule.find("layoutopt:")) != std::string::npos) { - std::string opt = rule.substr(delim + 10); - if (!opt.contains(":")) { - // invalid - Debug::log(ERR, "Invalid workspace rule found: {}", rule); - parseError = "Invalid workspace rule found: " + rule; - return; - } - - std::string val = opt.substr(opt.find(":") + 1); - opt = opt.substr(0, opt.find(":")); - - wsRule.layoutopts[opt] = val; - } - }; - - size_t pos = 0; - std::string rule; - while ((pos = rules.find(',')) != std::string::npos) { - rule = rules.substr(0, pos); - assignRule(rule); - rules.erase(0, pos + 1); - } - assignRule(rules); // match remaining rule - - wsRule.workspaceId = id; - wsRule.workspaceName = name; - - const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); - - if (IT == m_dWorkspaceRules.end()) - m_dWorkspaceRules.emplace_back(wsRule); - else - *IT = wsRule; -} - -void CConfigManager::handleSubmap(const std::string& command, const std::string& submap) { - if (submap == "reset") - m_szCurrentSubmap = ""; - else - m_szCurrentSubmap = submap; -} - -void CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { - if (rawpath.length() < 2) { - Debug::log(ERR, "source= path garbage"); - parseError = "source path " + rawpath + " bogus!"; - return; - } - std::unique_ptr glob_buf{new glob_t, [](glob_t* g) { globfree(g); }}; - memset(glob_buf.get(), 0, sizeof(glob_t)); - - if (auto r = glob(absolutePath(rawpath, configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { - parseError = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); - Debug::log(ERR, "{}", parseError); - return; - } - - for (size_t i = 0; i < glob_buf->gl_pathc; i++) { - auto value = absolutePath(glob_buf->gl_pathv[i], configCurrentPath); - - if (!std::filesystem::is_regular_file(value)) { - if (std::filesystem::exists(value)) { - Debug::log(WARN, "source= skipping non-file {}", value); - continue; - } - - Debug::log(ERR, "source= file doesnt exist"); - parseError = "source file " + value + " doesn't exist!"; - return; - } - configPaths.push_back(value); - - struct stat fileStat; - int err = stat(value.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at {}, error {}: {}", value, err, strerror(err)); - return; - } - - configModifyTimes[value] = fileStat.st_mtime; - - std::ifstream ifs; - ifs.open(value); - - std::string line = ""; - int linenum = 1; - if (ifs.is_open()) { - auto configCurrentPathBackup = configCurrentPath; - - while (std::getline(ifs, line)) { - // Read line by line. - try { - configCurrentPath = value; - parseLine(line); - } catch (...) { - Debug::log(ERR, "Error reading line from config. Line:"); - Debug::log(NONE, "{}", line.c_str()); - - parseError += "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): Line parsing error."; - } - - if (parseError != "" && !parseError.starts_with("Config error at line")) { - parseError = "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): " + parseError; - } - - ++linenum; - } - - ifs.close(); - - configCurrentPath = configCurrentPathBackup; - } - } -} - -void CConfigManager::handleBindWS(const std::string& command, const std::string& value) { - parseError = "bindws has been deprecated in favor of workspace rules, see the wiki -> workspace rules"; -} - -void CConfigManager::handleEnv(const std::string& command, const std::string& value) { - if (!isFirstLaunch) - return; - - const auto ARGS = CVarList(value, 2); - - if (ARGS[0].empty()) { - parseError = "env empty"; - return; - } - - setenv(ARGS[0].c_str(), ARGS[1].c_str(), 1); - - if (command.back() == 'd') { - // dbus - const auto CMD = -#ifdef USES_SYSTEMD - "systemctl --user import-environment " + ARGS[0] + - " && hash dbus-update-activation-environment 2>/dev/null && " -#endif - "dbus-update-activation-environment --systemd " + - ARGS[0]; - handleRawExec("", CMD); - } -} - -void CConfigManager::handlePlugin(const std::string& command, const std::string& path) { - if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) { - parseError = "plugin '" + path + "' declared twice"; - return; - } - - m_vDeclaredPlugins.push_back(path); -} - -std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) { - if (dynamic) { - parseError = ""; - currentCategory = ""; - } - - int needsLayoutRecalc = COMMAND == "monitor"; // 0 - no, 1 - yes, 2 - maybe - - if (COMMAND == "exec") { - if (isFirstLaunch) { - firstExecRequests.push_back(VALUE); - } else { - handleRawExec(COMMAND, VALUE); - } - } else if (COMMAND == "exec-once") { - if (isFirstLaunch) { - firstExecRequests.push_back(VALUE); - } - } else if (COMMAND == "monitor") - handleMonitor(COMMAND, VALUE); - else if (COMMAND.starts_with("bind")) - handleBind(COMMAND, VALUE); - else if (COMMAND == "unbind") - handleUnbind(COMMAND, VALUE); - else if (COMMAND == "workspace") - handleWorkspaceRules(COMMAND, VALUE); - else if (COMMAND == "windowrule") - handleWindowRule(COMMAND, VALUE); - else if (COMMAND == "windowrulev2") - handleWindowRuleV2(COMMAND, VALUE); - else if (COMMAND == "layerrule") - handleLayerRule(COMMAND, VALUE); - else if (COMMAND == "bezier") - handleBezier(COMMAND, VALUE); - else if (COMMAND == "animation") - handleAnimation(COMMAND, VALUE); - else if (COMMAND == "source") - handleSource(COMMAND, VALUE); - else if (COMMAND == "submap") - handleSubmap(COMMAND, VALUE); - else if (COMMAND == "blurls") - handleBlurLS(COMMAND, VALUE); - else if (COMMAND == "wsbind") - handleBindWS(COMMAND, VALUE); - else if (COMMAND == "plugin") - handlePlugin(COMMAND, VALUE); - else if (COMMAND.starts_with("env")) - handleEnv(COMMAND, VALUE); - else { - // try config - const auto IT = std::find_if(pluginKeywords.begin(), pluginKeywords.end(), [&](const auto& other) { return other.name == COMMAND; }); - - if (IT != pluginKeywords.end()) { - IT->fn(COMMAND, VALUE); - } else { - configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE); - needsLayoutRecalc = 2; - } - } - - if (dynamic) { - std::string retval = parseError; - parseError = ""; - - // invalidate layouts if they changed - if (needsLayoutRecalc) { - if (needsLayoutRecalc == 1 || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { - for (auto& m : g_pCompositor->m_vMonitors) - g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); - } - } - - // Update window border colors - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - - // manual crash - if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) { - m_bManualCrashInitiated = true; - if (g_pHyprNotificationOverlay) { - g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, - ICON_INFO); - } - } else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) { - // cowabunga it is - g_pHyprRenderer->initiateManualCrash(); - } - - return retval; - } - - return parseError; -} - -void CConfigManager::applyUserDefinedVars(std::string& line, const size_t equalsPlace) { - auto dollarPlace = line.find_first_of('$', equalsPlace); - - int times = 0; - - while (dollarPlace != std::string::npos) { - times++; - - const auto STRAFTERDOLLAR = line.substr(dollarPlace + 1); - bool found = false; - for (auto& [var, value] : configDynamicVars) { - if (STRAFTERDOLLAR.starts_with(var)) { - line.replace(dollarPlace, var.length() + 1, value); - found = true; - break; - } - } - - if (!found) { - // maybe env? - for (auto& [var, value] : environmentVariables) { - if (STRAFTERDOLLAR.starts_with(var)) { - line.replace(dollarPlace, var.length() + 1, value); - break; - } - } - } - - dollarPlace = line.find_first_of('$', dollarPlace + 1); - - if (times > 256 /* arbitrary limit */) { - line = ""; - parseError = "Maximum variable recursion limit hit. Evaluating the line led to too many variable substitutions."; - Debug::log(ERR, "Variable recursion limit hit in configmanager"); - break; - } - } -} - -void CConfigManager::parseLine(std::string& line) { - // first check if its not a comment - if (line[0] == '#') - return; - - // now, cut the comment off. ## is an escape. - for (long unsigned int i = 1; i < line.length(); ++i) { - if (line[i] == '#') { - if (i + 1 < line.length() && line[i + 1] != '#') { - line = line.substr(0, i); - break; // no need to parse more - } - - i++; - } - } - - size_t startPos = 0; - while ((startPos = line.find("##", startPos)) != std::string::npos && startPos < line.length() - 1 && startPos > 0) { - line.replace(startPos, 2, "#"); - startPos++; - } - - line = removeBeginEndSpacesTabs(line); - - if (line.contains(" {")) { - auto cat = line.substr(0, line.find(" {")); - transform(cat.begin(), cat.end(), cat.begin(), ::tolower); - std::replace(cat.begin(), cat.end(), ' ', '-'); - if (currentCategory.length() != 0) { - currentCategory.push_back(':'); - currentCategory.append(cat); - } else { - currentCategory = cat; - } - - return; - } - - if (line.contains("}") && currentCategory != "") { - - const auto LASTSEP = currentCategory.find_last_of(':'); - - if (LASTSEP == std::string::npos || currentCategory.starts_with("device")) - currentCategory = ""; - else - currentCategory = currentCategory.substr(0, LASTSEP); - - return; - } - - // And parse - // check if command - const auto EQUALSPLACE = line.find_first_of('='); - - // apply vars - applyUserDefinedVars(line, EQUALSPLACE); - - if (EQUALSPLACE == std::string::npos) - return; - - const auto COMMAND = removeBeginEndSpacesTabs(line.substr(0, EQUALSPLACE)); - const auto VALUE = removeBeginEndSpacesTabs(line.substr(EQUALSPLACE + 1)); - // - - parseKeyword(COMMAND, VALUE); -} - -void CConfigManager::loadConfigLoadVars() { - EMIT_HOOK_EVENT("preConfigReload", nullptr); - - Debug::log(LOG, "Reloading the config!"); - parseError = ""; // reset the error - currentCategory = ""; // reset the category - - // reset all vars before loading - setDefaultVars(); +std::optional CConfigManager::resetHLConfig() { m_dMonitorRules.clear(); m_dWindowRules.clear(); g_pKeybindManager->clearKeybinds(); g_pAnimationManager->removeAllBeziers(); m_mAdditionalReservedAreas.clear(); - configDynamicVars.clear(); - deviceConfigs.clear(); m_dBlurLSNamespaces.clear(); m_dWorkspaceRules.clear(); setDefaultAnimationVars(); // reset anims @@ -1656,82 +707,12 @@ void CConfigManager::loadConfigLoadVars() { Debug::log(LOG, "Using config: {}", mainConfigPath); configPaths.push_back(mainConfigPath); - if (g_pCompositor->explicitConfigPath.empty() && !std::filesystem::exists(mainConfigPath)) { - std::string configPath = std::filesystem::path(mainConfigPath).parent_path(); + const auto RET = verifyConfigExists(); - if (!std::filesystem::is_directory(configPath)) { - Debug::log(WARN, "Creating config home directory"); - try { - std::filesystem::create_directories(configPath); - } catch (...) { - parseError = "Broken config file! (Could not create config directory)"; - return; - } - } - - Debug::log(WARN, "No config file found; attempting to generate."); - std::ofstream ofs; - ofs.open(mainConfigPath, std::ios::trunc); - ofs << AUTOCONFIG; - ofs.close(); - } - - std::ifstream ifs; - ifs.open(mainConfigPath); - - if (!ifs.good()) { - ifs.close(); - - if (!g_pCompositor->explicitConfigPath.empty()) { - Debug::log(WARN, "Config reading error!"); - parseError = "Broken config file! (Could not read)"; - return; - } - - Debug::log(WARN, "Config reading error. Attempting to generate, backing up old one if exists"); - - if (std::filesystem::exists(mainConfigPath)) - std::filesystem::rename(mainConfigPath, mainConfigPath + ".backup"); - - // Create default config - std::ofstream ofs; - ofs.open(mainConfigPath, std::ios::trunc); - ofs << AUTOCONFIG; - ofs.close(); - - // Try to re-open - ifs.open(mainConfigPath); - if (!ifs.good()) { - parseError = "Broken config file! (Could not open)"; - return; - } - } - - std::string line = ""; - int linenum = 1; - if (ifs.is_open()) { - while (std::getline(ifs, line)) { - // Read line by line. - try { - configCurrentPath = mainConfigPath; - parseLine(line); - } catch (...) { - Debug::log(ERR, "Error reading line from config. Line:"); - Debug::log(NONE, "{}", line); - - parseError += "Config error at line " + std::to_string(linenum) + " (" + mainConfigPath + "): Line parsing error."; - } - - if (parseError != "" && !parseError.starts_with("Config error at line")) { - parseError = "Config error at line " + std::to_string(linenum) + " (" + mainConfigPath + "): " + parseError; - } - - ++linenum; - } - - ifs.close(); - } + return RET; +} +void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { for (auto& w : g_pCompositor->m_vWindows) { w->uncacheWindowDecos(); } @@ -1751,10 +732,10 @@ void CConfigManager::loadConfigLoadVars() { g_pHyprOpenGL->m_bReloadScreenShader = true; // parseError will be displayed next frame - if (parseError != "" && !configValues["debug:suppress_errors"].intValue) - g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); - else if (configValues["autogenerated"].intValue == 1) - g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + mainConfigPath + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland", + if (result.error && !std::any_cast(m_pConfig->getConfigValue("debug:suppress_errors"))) + g_pHyprError->queueCreate(result.getError(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); + else if (std::any_cast(m_pConfig->getConfigValue("autogenerated")) == 1) + g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + getMainConfigPath() + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland", CColor(1.0, 1.0, 70.0 / 255.0, 1.0)); else g_pHyprError->destroy(); @@ -1786,18 +767,18 @@ void CConfigManager::loadConfigLoadVars() { g_pCompositor->updateAllWindowsAnimatedDecorationValues(); // update layout - g_pLayoutManager->switchToLayout(configValues["general:layout"].strValue); + g_pLayoutManager->switchToLayout(std::any_cast(m_pConfig->getConfigValue("general:layout"))); // manual crash - if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) { + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { m_bManualCrashInitiated = true; g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, ICON_INFO); - } else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) { + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { // cowabunga it is g_pHyprRenderer->initiateManualCrash(); } - Debug::disableStdout = !configValues["debug:enable_stdout_logs"].intValue; + Debug::disableStdout = !std::any_cast(m_pConfig->getConfigValue("debug:enable_stdout_logs")); if (Debug::disableStdout && isFirstLaunch) Debug::log(LOG, "Disabling stdout logs! Check the log for further logs."); @@ -1822,6 +803,49 @@ void CConfigManager::loadConfigLoadVars() { g_pEventManager->postEvent(SHyprIPCEvent{"configreloaded", ""}); } +void CConfigManager::init() { + + const std::string CONFIGPATH = getMainConfigPath(); + reload(); + + struct stat fileStat; + int err = stat(CONFIGPATH.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at statting config, error {}", errno); + } + + configModifyTimes[CONFIGPATH] = fileStat.st_mtime; + + isFirstLaunch = false; +} + +std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) { + const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str()); + + // invalidate layouts if they changed + if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { + for (auto& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); + } + + // Update window border colors + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + // manual crash + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { + m_bManualCrashInitiated = true; + if (g_pHyprNotificationOverlay) { + g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, + ICON_INFO); + } + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { + // cowabunga it is + g_pHyprRenderer->initiateManualCrash(); + } + + return RET.error ? RET.getError() : ""; +} + void CConfigManager::tick() { std::string CONFIGPATH = getMainConfigPath(); if (!std::filesystem::exists(CONFIGPATH)) { @@ -1849,76 +873,35 @@ void CConfigManager::tick() { if (parse) { m_bForceReload = false; - loadConfigLoadVars(); + reload(); } } -std::mutex configmtx; -SConfigValue CConfigManager::getConfigValueSafe(const std::string& val) { - std::lock_guard lg(configmtx); +Hyprlang::CConfigValue* CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) { - SConfigValue copy = configValues[val]; + const auto VAL = m_pConfig->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str()); - return copy; -} - -SConfigValue CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) { - std::lock_guard lg(configmtx); - - const auto it = deviceConfigs.find(dev); - - if (it == deviceConfigs.end()) { - if (fallback.empty()) { - Debug::log(ERR, "getConfigValueSafeDevice: No device config for {} found???", dev); - return SConfigValue(); - } - return configValues[fallback]; + if ((!VAL || !VAL->m_bSetByUser) && !fallback.empty()) { + return m_pConfig->getConfigValuePtr(fallback.c_str()); } - const SConfigValue DEVICECONFIG = it->second[val]; - - if (!DEVICECONFIG.set && !fallback.empty()) { - return configValues[fallback]; - } - - return DEVICECONFIG; -} - -int CConfigManager::getInt(const std::string& v) { - return getConfigValueSafe(v).intValue; -} - -float CConfigManager::getFloat(const std::string& v) { - return getConfigValueSafe(v).floatValue; -} - -Vector2D CConfigManager::getVec(const std::string& v) { - return getConfigValueSafe(v).vecValue; -} - -std::string CConfigManager::getString(const std::string& v) { - auto VAL = getConfigValueSafe(v).strValue; - - if (VAL == STRVAL_EMPTY) - return ""; - return VAL; } int CConfigManager::getDeviceInt(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).intValue; + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); } float CConfigManager::getDeviceFloat(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).floatValue; + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); } Vector2D CConfigManager::getDeviceVec(const std::string& dev, const std::string& v, const std::string& fallback) { - return getConfigValueSafeDevice(dev, v, fallback).vecValue; + return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); } std::string CConfigManager::getDeviceString(const std::string& dev, const std::string& v, const std::string& fallback) { - auto VAL = getConfigValueSafeDevice(dev, v, fallback).strValue; + const auto VAL = std::string{std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue())}; if (VAL == STRVAL_EMPTY) return ""; @@ -1926,49 +909,21 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s return VAL; } -void CConfigManager::setInt(const std::string& v, int val) { - configValues[v].intValue = val; -} - -void CConfigManager::setFloat(const std::string& v, float val) { - configValues[v].floatValue = val; -} - -void CConfigManager::setVec(const std::string& v, Vector2D val) { - configValues[v].vecValue = val; -} - -void CConfigManager::setString(const std::string& v, const std::string& val) { - configValues[v].strValue = val; -} - -SMonitorRule CConfigManager::getMonitorRuleFor(const std::string& name, const std::string& displayName) { - SMonitorRule* found = nullptr; - +SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { for (auto& r : m_dMonitorRules) { - if (r.name == name || - (r.name.starts_with("desc:") && - (r.name.substr(5) == displayName || r.name.substr(5) == removeBeginEndSpacesTabs(displayName.substr(0, displayName.find_first_of('(')))))) { - found = &r; - break; + if (PMONITOR.matchesStaticSelector(r.name)) { + return r; } } - if (found) - return *found; - - Debug::log(WARN, "No rule found for {}, trying to use the first.", name); + Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR.szName); for (auto& r : m_dMonitorRules) { if (r.name == "") { - found = &r; - break; + return r; } } - if (found) - return *found; - Debug::log(WARN, "No rules configured. Using the default hardcoded one."); return SMonitorRule{.name = "", .resolution = Vector2D(0, 0), .offset = Vector2D(-INT32_MAX, -INT32_MAX), .scale = -1}; // 0, 0 is preferred and -1, -1 is auto @@ -1987,7 +942,7 @@ SWorkspaceRule CConfigManager::getWorkspaceRuleFor(CWorkspace* pWorkspace) { return *IT; } -std::vector CConfigManager::getMatchingRules(CWindow* pWindow) { +std::vector CConfigManager::getMatchingRules(CWindow* pWindow, bool dynamic) { if (!g_pCompositor->windowValidMapped(pWindow)) return std::vector(); @@ -2113,6 +1068,9 @@ std::vector CConfigManager::getMatchingRules(CWindow* pWindow) { returns.push_back(rule); + if (dynamic) + continue; + if (rule.szRule == "float") hasFloating = true; else if (rule.szRule == "fullscreen") @@ -2205,7 +1163,7 @@ void CConfigManager::performMonitorReload() { if (!m->output || m->isUnsafeFallback) continue; - auto rule = getMonitorRuleFor(m->szName, m->szDescription); + auto rule = getMonitorRuleFor(*m); if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { overAgain = true; @@ -2226,49 +1184,25 @@ void CConfigManager::performMonitorReload() { EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); } -SConfigValue* CConfigManager::getConfigValuePtr(const std::string& val) { - return &configValues[val]; +void* const* CConfigManager::getConfigValuePtr(const std::string& val) { + const auto VAL = m_pConfig->getConfigValuePtr(val.c_str()); + if (!VAL) + return nullptr; + return VAL->getDataStaticPtr(); } -SConfigValue* CConfigManager::getConfigValuePtrSafe(const std::string& val) { - if (val.starts_with("device:")) { - const auto DEVICE = val.substr(7, val.find_last_of(':') - 7); - const auto CONFIGVAR = val.substr(val.find_last_of(':') + 1); +Hyprlang::CConfigValue* CConfigManager::getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat) { + if (!specialCat.empty()) + return m_pConfig->getSpecialConfigValuePtr(specialCat.c_str(), name.c_str(), nullptr); - const auto DEVICECONF = deviceConfigs.find(DEVICE); - if (DEVICECONF == deviceConfigs.end()) - return nullptr; - - const auto IT = DEVICECONF->second.find(CONFIGVAR); - - if (IT == DEVICECONF->second.end()) - return nullptr; - - return &IT->second; - } else if (val.starts_with("plugin:")) { - for (auto& [pl, pMap] : pluginConfigs) { - const auto IT = pMap->find(val); - - if (IT != pMap->end()) - return &IT->second; - } - - return nullptr; - } - - const auto IT = configValues.find(val); - - if (IT == configValues.end()) - return nullptr; - - return &(IT->second); + return m_pConfig->getConfigValuePtr(name.c_str()); } bool CConfigManager::deviceConfigExists(const std::string& dev) { auto copy = dev; std::replace(copy.begin(), copy.end(), ' ', '-'); - return deviceConfigs.contains(copy); + return m_pConfig->specialCategoryExistsForKey("device", copy.c_str()); } bool CConfigManager::shouldBlurLS(const std::string& ns) { @@ -2286,7 +1220,7 @@ void CConfigManager::ensureMonitorStatus() { if (!rm->output || rm->isUnsafeFallback) continue; - auto rule = getMonitorRuleFor(rm->szName, rm->szDescription); + auto rule = getMonitorRuleFor(*rm); if (rule.disabled == rm->m_bEnabled) g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); @@ -2294,33 +1228,33 @@ void CConfigManager::ensureMonitorStatus() { } void CConfigManager::ensureVRR(CMonitor* pMonitor) { - static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue; + static auto* const PVRR = reinterpret_cast(getConfigValuePtr("misc:vrr")); static auto ensureVRRForDisplay = [&](CMonitor* m) -> void { - if (!m->output) + if (!m->output || m->createdByUser) return; - const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : *PVRR; + const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : **PVRR; if (USEVRR == 0) { if (m->vrrActive) { - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } m->vrrActive = false; return; } else if (USEVRR == 1) { if (!m->vrrActive) { - wlr_output_enable_adaptive_sync(m->output, 1); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - if (!wlr_output_test(m->output)) { + if (!m->state.test()) { Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); } - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); } m->vrrActive = true; @@ -2337,20 +1271,20 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) { const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; if (WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED) { - wlr_output_enable_adaptive_sync(m->output, 1); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 1); - if (!wlr_output_test(m->output)) { + if (!m->state.test()) { Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); } - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); } else if (!WORKSPACEFULL && m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED) { - wlr_output_enable_adaptive_sync(m->output, 0); + wlr_output_state_set_adaptive_sync_enabled(m->state.wlr(), 0); - if (!wlr_output_commit(m->output)) + if (!m->state.commit()) Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } } @@ -2371,10 +1305,7 @@ SAnimationPropertyConfig* CConfigManager::getAnimationPropertyConfig(const std:: } void CConfigManager::addParseError(const std::string& err) { - if (parseError == "") - parseError = err; - - g_pHyprError->queueCreate(parseError + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); + g_pHyprError->queueCreate(err + "\nHyprland may not work correctly.", CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); } CMonitor* CConfigManager::getBoundMonitorForWS(const std::string& wsname) { @@ -2438,30 +1369,41 @@ std::unordered_map CConfigManager::getAni return animationConfig; } -void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value) { - auto CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; }); - - if (CONFIGMAPIT == pluginConfigs.end()) { - pluginConfigs.emplace( - std::pair>>(handle, std::make_unique>())); - CONFIGMAPIT = std::find_if(pluginConfigs.begin(), pluginConfigs.end(), [&](const auto& other) { return other.first == handle; }); - } - - (*CONFIGMAPIT->second)[name] = value; - - if (const auto IT = std::find_if(m_vFailedPluginConfigValues.begin(), m_vFailedPluginConfigValues.end(), [&](const auto& other) { return other.first == name; }); - IT != m_vFailedPluginConfigValues.end()) { - configSetValueSafe(IT->first, IT->second); - } +void onPluginLoadUnload(const std::string& name, bool load) { + // } -void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, std::function fn) { +void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value) { + if (!name.starts_with("plugin:")) + return; + + std::string field = name.substr(7); + + m_pConfig->addSpecialConfigValue("plugin", field.c_str(), value); + pluginVariables.push_back({handle, field}); +} + +void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts) { pluginKeywords.emplace_back(SPluginKeyword{handle, name, fn}); + m_pConfig->registerHandler(fn, name.c_str(), opts); } void CConfigManager::removePluginConfig(HANDLE handle) { - std::erase_if(pluginConfigs, [&](const auto& other) { return other.first == handle; }); + for (auto& k : pluginKeywords) { + if (k.handle != handle) + continue; + + m_pConfig->unregisterHandler(k.name.c_str()); + } + std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; }); + for (auto& [h, n] : pluginVariables) { + if (h != handle) + continue; + + m_pConfig->removeSpecialConfigValue("plugin", n.c_str()); + } + std::erase_if(pluginVariables, [handle](const auto& other) { return other.handle == handle; }); } std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { @@ -2478,3 +1420,876 @@ std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { } return ""; } + +std::optional CConfigManager::handleRawExec(const std::string& command, const std::string& args) { + if (isFirstLaunch) { + firstExecRequests.push_back(args); + return {}; + } + + g_pKeybindManager->spawn(args); + return {}; +} + +std::optional CConfigManager::handleExecOnce(const std::string& command, const std::string& args) { + if (isFirstLaunch) + firstExecRequests.push_back(args); + + return {}; +} + +static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { + auto args = CVarList(modeline, 0, 's'); + + auto keyword = args[0]; + std::transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower); + + if (keyword != "modeline") + return false; + + if (args.size() < 10) { + Debug::log(ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); + return false; + } + + int argno = 1; + + mode.type = DRM_MODE_TYPE_USERDEF; + mode.clock = std::stof(args[argno++]) * 1000; + mode.hdisplay = std::stoi(args[argno++]); + mode.hsync_start = std::stoi(args[argno++]); + mode.hsync_end = std::stoi(args[argno++]); + mode.htotal = std::stoi(args[argno++]); + mode.vdisplay = std::stoi(args[argno++]); + mode.vsync_start = std::stoi(args[argno++]); + mode.vsync_end = std::stoi(args[argno++]); + mode.vtotal = std::stoi(args[argno++]); + mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; + + // clang-format off + static std::unordered_map flagsmap = { + {"+hsync", DRM_MODE_FLAG_PHSYNC}, + {"-hsync", DRM_MODE_FLAG_NHSYNC}, + {"+vsync", DRM_MODE_FLAG_PVSYNC}, + {"-vsync", DRM_MODE_FLAG_NVSYNC}, + {"Interlace", DRM_MODE_FLAG_INTERLACE}, + }; + // clang-format on + + for (; argno < static_cast(args.size()); argno++) { + auto key = args[argno]; + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + + auto it = flagsmap.find(key); + + if (it != flagsmap.end()) + mode.flags |= it->second; + else + Debug::log(ERR, "invalid flag {} in modeline", it->first); + } + + snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); + + return true; +} + +std::optional CConfigManager::handleMonitor(const std::string& command, const std::string& args) { + + // get the monitor config + SMonitorRule newrule; + + const auto ARGS = CVarList(args); + + newrule.name = ARGS[0]; + + if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") { + if (ARGS[1] == "disable" || ARGS[1] == "disabled") + newrule.disabled = true; + else if (ARGS[1] == "transform") { + const auto TSF = std::stoi(ARGS[2]); + if (std::clamp(TSF, 0, 7) != TSF) { + Debug::log(ERR, "invalid transform {} in monitor", TSF); + return "invalid transform"; + } + + const auto TRANSFORM = (wl_output_transform)TSF; + + // overwrite if exists + for (auto& r : m_dMonitorRules) { + if (r.name == newrule.name) { + r.transform = TRANSFORM; + return {}; + } + } + + return {}; + } else if (ARGS[1] == "addreserved") { + int top = std::stoi(ARGS[2]); + + int bottom = std::stoi(ARGS[3]); + + int left = std::stoi(ARGS[4]); + + int right = std::stoi(ARGS[5]); + + m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right}; + + return {}; + } else { + Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); + return "parse error: curitem bogus"; + } + + std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); + + m_dMonitorRules.push_back(newrule); + + return {}; + } + + if (ARGS[1].starts_with("pref")) { + newrule.resolution = Vector2D(); + } else if (ARGS[1].starts_with("highrr")) { + newrule.resolution = Vector2D(-1, -1); + } else if (ARGS[1].starts_with("highres")) { + newrule.resolution = Vector2D(-1, -2); + } else if (parseModeLine(ARGS[1], newrule.drmMode)) { + newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); + newrule.refreshRate = newrule.drmMode.vrefresh / 1000; + } else { + newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x'))); + newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@'))); + + if (ARGS[1].contains("@")) + newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1)); + } + + if (ARGS[2].starts_with("auto")) { + newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); + } else { + newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x'))); + newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1)); + } + + std::string error = ""; + + if (ARGS[3].starts_with("auto")) { + newrule.scale = -1; + } else { + newrule.scale = stof(ARGS[3]); + + if (newrule.scale < 0.25f) { + error = "invalid scale"; + newrule.scale = 1; + } + } + + int argno = 4; + + while (ARGS[argno] != "") { + if (ARGS[argno] == "mirror") { + newrule.mirrorOf = ARGS[argno + 1]; + argno++; + } else if (ARGS[argno] == "bitdepth") { + newrule.enable10bit = ARGS[argno + 1] == "10"; + argno++; + } else if (ARGS[argno] == "transform") { + newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]); + argno++; + } else if (ARGS[argno] == "vrr") { + newrule.vrr = std::stoi(ARGS[argno + 1]); + argno++; + } else if (ARGS[argno] == "workspace") { + std::string name = ""; + int wsId = getWorkspaceIDFromString(ARGS[argno + 1], name); + + SWorkspaceRule wsRule; + wsRule.monitor = newrule.name; + wsRule.workspaceString = ARGS[argno + 1]; + wsRule.workspaceName = name; + wsRule.workspaceId = wsId; + + m_dWorkspaceRules.emplace_back(wsRule); + argno++; + } else { + Debug::log(ERR, "Config error: invalid monitor syntax"); + return "invalid syntax at \"" + ARGS[argno] + "\""; + } + + argno++; + } + + std::erase_if(m_dMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); + + m_dMonitorRules.push_back(newrule); + + if (error.empty()) + return {}; + return error; +} + +std::optional CConfigManager::handleBezier(const std::string& command, const std::string& args) { + const auto ARGS = CVarList(args); + + std::string bezierName = ARGS[0]; + + if (ARGS[1] == "") + return "too few arguments"; + float p1x = std::stof(ARGS[1]); + + if (ARGS[2] == "") + return "too few arguments"; + float p1y = std::stof(ARGS[2]); + + if (ARGS[3] == "") + return "too few arguments"; + float p2x = std::stof(ARGS[3]); + + if (ARGS[4] == "") + return "too few arguments"; + float p2y = std::stof(ARGS[4]); + + if (ARGS[5] != "") + return "too many arguments"; + + g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y)); + + return {}; +} + +void CConfigManager::setAnimForChildren(SAnimationPropertyConfig* const ANIM) { + for (auto& [name, anim] : animationConfig) { + if (anim.pParentAnimation == ANIM && !anim.overridden) { + // if a child isnt overridden, set the values of the parent + anim.pValues = ANIM->pValues; + + setAnimForChildren(&anim); + } + } +}; + +std::optional CConfigManager::handleAnimation(const std::string& command, const std::string& args) { + const auto ARGS = CVarList(args); + + // Master on/off + + // anim name + const auto ANIMNAME = ARGS[0]; + + const auto PANIM = animationConfig.find(ANIMNAME); + + if (PANIM == animationConfig.end()) + return "no such animation"; + + PANIM->second.overridden = true; + PANIM->second.pValues = &PANIM->second; + + // on/off + PANIM->second.internalEnabled = ARGS[1] == "1"; + + if (ARGS[1] != "0" && ARGS[1] != "1") + return "invalid animation on/off state"; + + if (PANIM->second.internalEnabled) { + // speed + if (isNumber(ARGS[2], true)) { + PANIM->second.internalSpeed = std::stof(ARGS[2]); + + if (PANIM->second.internalSpeed <= 0) { + PANIM->second.internalSpeed = 1.f; + return "invalid speed"; + } + } else { + PANIM->second.internalSpeed = 10.f; + return "invalid speed"; + } + + // curve + PANIM->second.internalBezier = ARGS[3]; + + if (!g_pAnimationManager->bezierExists(ARGS[3])) { + PANIM->second.internalBezier = "default"; + return "no such bezier"; + } + + // style + PANIM->second.internalStyle = ARGS[4]; + + if (ARGS[4] != "") { + const auto ERR = g_pAnimationManager->styleValidInConfigVar(ANIMNAME, ARGS[4]); + + if (ERR != "") + return ERR; + } + } + + // now, check for children, recursively + setAnimForChildren(&PANIM->second); + + return {}; +} + +std::optional CConfigManager::handleBind(const std::string& command, const std::string& value) { + // example: + // bind[fl]=SUPER,G,exec,dmenu_run + + // flags + bool locked = false; + bool release = false; + bool repeat = false; + bool mouse = false; + bool nonConsuming = false; + bool transparent = false; + bool ignoreMods = false; + const auto BINDARGS = command.substr(4); + + for (auto& arg : BINDARGS) { + if (arg == 'l') { + locked = true; + } else if (arg == 'r') { + release = true; + } else if (arg == 'e') { + repeat = true; + } else if (arg == 'm') { + mouse = true; + } else if (arg == 'n') { + nonConsuming = true; + } else if (arg == 't') { + transparent = true; + } else if (arg == 'i') { + ignoreMods = true; + } else { + return "bind: invalid flag"; + } + } + + if (release && repeat) + return "flags r and e are mutually exclusive"; + + if (mouse && (repeat || release || locked)) + return "flag m is exclusive"; + + const auto ARGS = CVarList(value, 4); + + if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse)) + return "bind: too few args"; + else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse)) + return "bind: too many args"; + + const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); + const auto MODSTR = ARGS[0]; + + const auto KEY = ARGS[1]; + + auto HANDLER = ARGS[2]; + + const auto COMMAND = mouse ? HANDLER : ARGS[3]; + + if (mouse) + HANDLER = "mouse"; + + // to lower + std::transform(HANDLER.begin(), HANDLER.end(), HANDLER.begin(), ::tolower); + + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(HANDLER); + + if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) { + Debug::log(ERR, "Invalid dispatcher!"); + return "Invalid dispatcher, requested \"" + HANDLER + "\" does not exist"; + } + + if (MOD == 0 && MODSTR != "") { + Debug::log(ERR, "Invalid mod!"); + return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; + } + + if (KEY != "") { + if (isNumber(KEY) && std::stoi(KEY) > 9) + g_pKeybindManager->addKeybind( + SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + else if (KEY.starts_with("code:") && isNumber(KEY.substr(5))) + g_pKeybindManager->addKeybind( + SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + else + g_pKeybindManager->addKeybind(SKeybind{KEY, 0, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods}); + } + + return {}; +} + +std::optional CConfigManager::handleUnbind(const std::string& command, const std::string& value) { + const auto ARGS = CVarList(value); + + const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); + + const auto KEY = ARGS[1]; + + g_pKeybindManager->removeKeybind(MOD, KEY); + + return {}; +} + +bool windowRuleValid(const std::string& RULE) { + return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || + RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || + RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "fakefullscreen" || + RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" || + RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || RULE == "forcergbx" || + RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") || + RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent"); +} + +bool layerRuleValid(const std::string& RULE) { + return RULE == "noanim" || RULE == "blur" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE.starts_with("xray"); +} + +std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); + + // check rule and value + if (RULE == "" || VALUE == "") + return "empty rule?"; + + if (RULE == "unset") { + std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { return other.szValue == VALUE; }); + return {}; + } + + // verify we support a rule + if (!windowRuleValid(RULE)) { + Debug::log(ERR, "Invalid rule found: {}", RULE); + return "Invalid rule: " + RULE; + } + + if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) + m_dWindowRules.push_front({RULE, VALUE}); + else + m_dWindowRules.push_back({RULE, VALUE}); + + return {}; +} + +std::optional CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); + + // check rule and value + if (RULE == "" || VALUE == "") + return "empty rule?"; + + if (RULE == "unset") { + std::erase_if(m_dLayerRules, [&](const SLayerRule& other) { return other.targetNamespace == VALUE; }); + return {}; + } + + if (!layerRuleValid(RULE)) { + Debug::log(ERR, "Invalid rule found: {}", RULE); + return "Invalid rule found: " + RULE; + } + + m_dLayerRules.push_back({VALUE, RULE}); + + for (auto& m : g_pCompositor->m_vMonitors) + for (auto& lsl : m->m_aLayerSurfaceLayers) + for (auto& ls : lsl) + ls->applyRules(); + + return {}; +} + +std::optional CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) { + const auto RULE = removeBeginEndSpacesTabs(value.substr(0, value.find_first_of(','))); + const auto VALUE = value.substr(value.find_first_of(',') + 1); + + if (!windowRuleValid(RULE) && RULE != "unset") { + Debug::log(ERR, "Invalid rulev2 found: {}", RULE); + return "Invalid rulev2 found: " + RULE; + } + + // now we estract shit from the value + SWindowRule rule; + rule.v2 = true; + rule.szRule = RULE; + rule.szValue = VALUE; + + const auto TITLEPOS = VALUE.find("title:"); + const auto CLASSPOS = VALUE.find("class:"); + const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); + const auto INITIALCLASSPOS = VALUE.find("initialClass:"); + const auto X11POS = VALUE.find("xwayland:"); + const auto FLOATPOS = VALUE.find("floating:"); + const auto FULLSCREENPOS = VALUE.find("fullscreen:"); + const auto PINNEDPOS = VALUE.find("pinned:"); + const auto FOCUSPOS = VALUE.find("focus:"); + const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); + + // find workspacepos that isn't onworkspacepos + size_t WORKSPACEPOS = std::string::npos; + size_t currentPos = VALUE.find("workspace:"); + while (currentPos != std::string::npos) { + if (currentPos == 0 || VALUE[currentPos - 1] != 'n') { + WORKSPACEPOS = currentPos; + break; + } + currentPos = VALUE.find("workspace:", currentPos + 1); + } + + if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && + X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && + FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { + Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); + return "Invalid rulev2 syntax: " + VALUE; + } + + auto extract = [&](size_t pos) -> std::string { + std::string result; + result = VALUE.substr(pos); + + size_t min = 999999; + if (TITLEPOS > pos && TITLEPOS < min) + min = TITLEPOS; + if (CLASSPOS > pos && CLASSPOS < min) + min = CLASSPOS; + if (INITIALTITLEPOS > pos && INITIALTITLEPOS < min) + min = INITIALTITLEPOS; + if (INITIALCLASSPOS > pos && INITIALCLASSPOS < min) + min = INITIALCLASSPOS; + if (X11POS > pos && X11POS < min) + min = X11POS; + if (FLOATPOS > pos && FLOATPOS < min) + min = FLOATPOS; + if (FULLSCREENPOS > pos && FULLSCREENPOS < min) + min = FULLSCREENPOS; + if (PINNEDPOS > pos && PINNEDPOS < min) + min = PINNEDPOS; + if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) + min = ONWORKSPACEPOS; + if (WORKSPACEPOS > pos && WORKSPACEPOS < min) + min = WORKSPACEPOS; + if (FOCUSPOS > pos && FOCUSPOS < min) + min = FOCUSPOS; + + result = result.substr(0, min - pos); + + result = removeBeginEndSpacesTabs(result); + + if (result.back() == ',') + result.pop_back(); + + return result; + }; + + if (CLASSPOS != std::string::npos) + rule.szClass = extract(CLASSPOS + 6); + + if (TITLEPOS != std::string::npos) + rule.szTitle = extract(TITLEPOS + 6); + + if (INITIALCLASSPOS != std::string::npos) + rule.szInitialClass = extract(INITIALCLASSPOS + 13); + + if (INITIALTITLEPOS != std::string::npos) + rule.szInitialTitle = extract(INITIALTITLEPOS + 13); + + if (X11POS != std::string::npos) + rule.bX11 = extract(X11POS + 9) == "1" ? 1 : 0; + + if (FLOATPOS != std::string::npos) + rule.bFloating = extract(FLOATPOS + 9) == "1" ? 1 : 0; + + if (FULLSCREENPOS != std::string::npos) + rule.bFullscreen = extract(FULLSCREENPOS + 11) == "1" ? 1 : 0; + + if (PINNEDPOS != std::string::npos) + rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; + + if (WORKSPACEPOS != std::string::npos) + rule.szWorkspace = extract(WORKSPACEPOS + 10); + + if (FOCUSPOS != std::string::npos) + rule.bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; + + if (ONWORKSPACEPOS != std::string::npos) + rule.iOnWorkspace = configStringToInt(extract(ONWORKSPACEPOS + 12)); + + if (RULE == "unset") { + std::erase_if(m_dWindowRules, [&](const SWindowRule& other) { + if (!other.v2) { + return other.szClass == rule.szClass && !rule.szClass.empty(); + } else { + if (!rule.szClass.empty() && rule.szClass != other.szClass) + return false; + + if (!rule.szTitle.empty() && rule.szTitle != other.szTitle) + return false; + + if (!rule.szInitialClass.empty() && rule.szInitialClass != other.szInitialClass) + return false; + + if (!rule.szInitialTitle.empty() && rule.szInitialTitle != other.szInitialTitle) + return false; + + if (rule.bX11 != -1 && rule.bX11 != other.bX11) + return false; + + if (rule.bFloating != -1 && rule.bFloating != other.bFloating) + return false; + + if (rule.bFullscreen != -1 && rule.bFullscreen != other.bFullscreen) + return false; + + if (rule.bPinned != -1 && rule.bPinned != other.bPinned) + return false; + + if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace) + return false; + + if (rule.bFocus != -1 && rule.bFocus != other.bFocus) + return false; + + if (rule.iOnWorkspace != -1 && rule.iOnWorkspace != other.iOnWorkspace) + return false; + + return true; + } + }); + return {}; + } + + if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) + m_dWindowRules.push_front(rule); + else + m_dWindowRules.push_back(rule); + + return {}; +} + +void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { + const bool BYADDRESS = name.starts_with("address:"); + std::string matchName = name; + + if (BYADDRESS) { + matchName = matchName.substr(8); + } + + for (auto& m : g_pCompositor->m_vMonitors) { + for (auto& lsl : m->m_aLayerSurfaceLayers) { + for (auto& ls : lsl) { + if (BYADDRESS) { + if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) + ls->forceBlur = forceBlur; + } else if (ls->szNamespace == matchName) + ls->forceBlur = forceBlur; + } + } + } +} + +std::optional CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { + if (value.starts_with("remove,")) { + const auto TOREMOVE = removeBeginEndSpacesTabs(value.substr(7)); + if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) + updateBlurredLS(TOREMOVE, false); + return {}; + } + + m_dBlurLSNamespaces.emplace_back(value); + updateBlurredLS(value, true); + + return {}; +} + +std::optional CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { + // This can either be the monitor or the workspace identifier + const auto FIRST_DELIM = value.find_first_of(','); + + std::string name = ""; + auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM)); + int id = getWorkspaceIDFromString(first_ident, name); + + auto rules = value.substr(FIRST_DELIM + 1); + SWorkspaceRule wsRule; + wsRule.workspaceString = first_ident; + if (id == WORKSPACE_INVALID) { + // it could be the monitor. If so, second value MUST be + // the workspace. + const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1); + auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); + id = getWorkspaceIDFromString(wsIdent, name); + if (id == WORKSPACE_INVALID) { + Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); + return "Invalid workspace identifier found: " + wsIdent; + } + wsRule.monitor = first_ident; + wsRule.workspaceString = wsIdent; + wsRule.isDefault = true; // backwards compat + rules = value.substr(WORKSPACE_DELIM + 1); + } + + const static std::string ruleOnCreatedEmtpy = "on-created-empty:"; + const static int ruleOnCreatedEmtpyLen = ruleOnCreatedEmtpy.length(); + + auto assignRule = [&](std::string rule) -> std::optional { + size_t delim = std::string::npos; + if ((delim = rule.find("gapsin:")) != std::string::npos) { + CVarList varlist = CVarList(rule.substr(delim + 7), 0, ' '); + wsRule.gapsIn = CCssGapData(); + try { + wsRule.gapsIn->parseGapData(varlist); + } catch (...) { return "Error parsing workspace rule gaps: {}", rule.substr(delim + 7); } + } else if ((delim = rule.find("gapsout:")) != std::string::npos) { + CVarList varlist = CVarList(rule.substr(delim + 8), 0, ' '); + wsRule.gapsOut = CCssGapData(); + try { + wsRule.gapsOut->parseGapData(varlist); + } catch (...) { return "Error parsing workspace rule gaps: {}", rule.substr(delim + 8); } + } else if ((delim = rule.find("bordersize:")) != std::string::npos) + try { + wsRule.borderSize = std::stoi(rule.substr(delim + 11)); + } catch (...) { return "Error parsing workspace rule bordersize: {}", rule.substr(delim + 11); } + else if ((delim = rule.find("border:")) != std::string::npos) + wsRule.border = configStringToInt(rule.substr(delim + 7)); + else if ((delim = rule.find("shadow:")) != std::string::npos) + wsRule.shadow = configStringToInt(rule.substr(delim + 7)); + else if ((delim = rule.find("rounding:")) != std::string::npos) + wsRule.rounding = configStringToInt(rule.substr(delim + 9)); + else if ((delim = rule.find("decorate:")) != std::string::npos) + wsRule.decorate = configStringToInt(rule.substr(delim + 9)); + else if ((delim = rule.find("monitor:")) != std::string::npos) + wsRule.monitor = rule.substr(delim + 8); + else if ((delim = rule.find("default:")) != std::string::npos) + wsRule.isDefault = configStringToInt(rule.substr(delim + 8)); + else if ((delim = rule.find("persistent:")) != std::string::npos) + wsRule.isPersistent = configStringToInt(rule.substr(delim + 11)); + else if ((delim = rule.find("defaultName:")) != std::string::npos) + wsRule.defaultName = rule.substr(delim + 12); + else if ((delim = rule.find(ruleOnCreatedEmtpy)) != std::string::npos) + wsRule.onCreatedEmptyRunCmd = cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmtpyLen)); + else if ((delim = rule.find("layoutopt:")) != std::string::npos) { + std::string opt = rule.substr(delim + 10); + if (!opt.contains(":")) { + // invalid + Debug::log(ERR, "Invalid workspace rule found: {}", rule); + return "Invalid workspace rule found: " + rule; + } + + std::string val = opt.substr(opt.find(":") + 1); + opt = opt.substr(0, opt.find(":")); + + wsRule.layoutopts[opt] = val; + } + + return {}; + }; + + CVarList rulesList{rules, 0, ',', true}; + for (auto& r : rulesList) { + const auto R = assignRule(r); + if (R.has_value()) + return R; + } + + wsRule.workspaceId = id; + wsRule.workspaceName = name; + + const auto IT = std::find_if(m_dWorkspaceRules.begin(), m_dWorkspaceRules.end(), [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); + + if (IT == m_dWorkspaceRules.end()) + m_dWorkspaceRules.emplace_back(wsRule); + else + *IT = wsRule; + + return {}; +} + +std::optional CConfigManager::handleSubmap(const std::string& command, const std::string& submap) { + if (submap == "reset") + m_szCurrentSubmap = ""; + else + m_szCurrentSubmap = submap; + + return {}; +} + +std::optional CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { + if (rawpath.length() < 2) { + Debug::log(ERR, "source= path garbage"); + return "source path " + rawpath + " bogus!"; + } + std::unique_ptr glob_buf{new glob_t, [](glob_t* g) { globfree(g); }}; + memset(glob_buf.get(), 0, sizeof(glob_t)); + + if (auto r = glob(absolutePath(rawpath, configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { + std::string err = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); + Debug::log(ERR, "{}", err); + return err; + } + + for (size_t i = 0; i < glob_buf->gl_pathc; i++) { + auto value = absolutePath(glob_buf->gl_pathv[i], configCurrentPath); + + if (!std::filesystem::is_regular_file(value)) { + if (std::filesystem::exists(value)) { + Debug::log(WARN, "source= skipping non-file {}", value); + continue; + } + + Debug::log(ERR, "source= file doesnt exist"); + return "source file " + value + " doesn't exist!"; + } + configPaths.push_back(value); + + struct stat fileStat; + int err = stat(value.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at {}, error {}: {}", value, err, strerror(err)); + return {}; + } + + configModifyTimes[value] = fileStat.st_mtime; + auto configCurrentPathBackup = configCurrentPath; + configCurrentPath = value; + + m_pConfig->parseFile(value.c_str()); + + configCurrentPath = configCurrentPathBackup; + } + + return {}; +} + +std::optional CConfigManager::handleEnv(const std::string& command, const std::string& value) { + if (!isFirstLaunch) + return {}; + + const auto ARGS = CVarList(value, 2); + + if (ARGS[0].empty()) + return "env empty"; + + setenv(ARGS[0].c_str(), ARGS[1].c_str(), 1); + + if (command.back() == 'd') { + // dbus + const auto CMD = +#ifdef USES_SYSTEMD + "systemctl --user import-environment " + ARGS[0] + + " && hash dbus-update-activation-environment 2>/dev/null && " +#endif + "dbus-update-activation-environment --systemd " + + ARGS[0]; + handleRawExec("", CMD); + } + + return {}; +} + +std::optional CConfigManager::handlePlugin(const std::string& command, const std::string& path) { + if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) + return "plugin '" + path + "' declared twice"; + + m_vDeclaredPlugins.push_back(path); + + return {}; +} diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index d5957cd5..fbc61059 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -21,21 +21,13 @@ #include "defaultConfig.hpp" #include "ConfigDataValues.hpp" +#include + #define INITANIMCFG(name) animationConfig[name] = {} #define CREATEANIMCFG(name, parent) animationConfig[name] = {false, "", "", 0.f, -1, &animationConfig["global"], &animationConfig[parent]} #define HANDLE void* -struct SConfigValue { - int64_t intValue = -INT64_MAX; - float floatValue = -__FLT_MAX__; - std::string strValue = ""; - Vector2D vecValue = Vector2D(-__FLT_MAX__, -__FLT_MAX__); - std::shared_ptr data; - - bool set = false; // used for device configs -}; - struct SWorkspaceRule { std::string monitor = ""; std::string workspaceString = ""; @@ -43,14 +35,15 @@ struct SWorkspaceRule { int workspaceId = -1; bool isDefault = false; bool isPersistent = false; - std::optional gapsIn; - std::optional gapsOut; + std::optional gapsIn; + std::optional gapsOut; std::optional borderSize; std::optional border; std::optional rounding; std::optional decorate; std::optional shadow; std::optional onCreatedEmptyRunCmd; + std::optional defaultName; std::map layoutopts; }; @@ -74,9 +67,14 @@ struct SAnimationPropertyConfig { }; struct SPluginKeyword { - HANDLE handle = 0; - std::string name = ""; - std::function fn; + HANDLE handle = 0; + std::string name = ""; + Hyprlang::PCONFIGHANDLERFUNC fn = nullptr; +}; + +struct SPluginVariable { + HANDLE handle = 0; + std::string name = ""; }; struct SExecRequestedRule { @@ -91,28 +89,21 @@ class CConfigManager { void tick(); void init(); - int getInt(const std::string&); - float getFloat(const std::string&); - Vector2D getVec(const std::string&); - std::string getString(const std::string&); - void setFloat(const std::string&, float); - void setInt(const std::string&, int); - void setVec(const std::string&, Vector2D); - void setString(const std::string&, const std::string&); - int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = ""); Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = ""); std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = ""); bool deviceConfigExists(const std::string&); + Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); bool shouldBlurLS(const std::string&); - SConfigValue* getConfigValuePtr(const std::string&); - SConfigValue* getConfigValuePtrSafe(const std::string&); + void* const* getConfigValuePtr(const std::string&); + Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); + void onPluginLoadUnload(const std::string& name, bool load); static std::string getConfigDir(); static std::string getMainConfigPath(); - SMonitorRule getMonitorRuleFor(const std::string&, const std::string& displayName = ""); + SMonitorRule getMonitorRuleFor(const CMonitor&); SWorkspaceRule getWorkspaceRuleFor(CWorkspace*); std::string getDefaultWorkspaceFor(const std::string&); @@ -120,15 +111,15 @@ class CConfigManager { std::string getBoundMonitorStringForWS(const std::string&); const std::deque& getAllWorkspaceRules(); - std::vector getMatchingRules(CWindow*); + std::vector getMatchingRules(CWindow*, bool dynamic = true); std::vector getMatchingRules(SLayerSurface*); std::unordered_map m_mAdditionalReservedAreas; std::unordered_map getAnimationConfig(); - void addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value); - void addPluginKeyword(HANDLE handle, const std::string& name, std::function fun); + void addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value); + void addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fun, Hyprlang::SHandlerOptions opts = {}); void removePluginConfig(HANDLE handle); // no-op when done. @@ -141,7 +132,7 @@ class CConfigManager { void ensureMonitorStatus(); void ensureVRR(CMonitor* pMonitor = nullptr); - std::string parseKeyword(const std::string&, const std::string&, bool dynamic = false); + std::string parseKeyword(const std::string&, const std::string&); void addParseError(const std::string&); @@ -151,77 +142,65 @@ class CConfigManager { void handlePluginLoads(); - std::string configCurrentPath; + // keywords + std::optional handleRawExec(const std::string&, const std::string&); + std::optional handleExecOnce(const std::string&, const std::string&); + std::optional handleMonitor(const std::string&, const std::string&); + std::optional handleBind(const std::string&, const std::string&); + std::optional handleUnbind(const std::string&, const std::string&); + std::optional handleWindowRule(const std::string&, const std::string&); + std::optional handleLayerRule(const std::string&, const std::string&); + std::optional handleWindowRuleV2(const std::string&, const std::string&); + std::optional handleWorkspaceRules(const std::string&, const std::string&); + std::optional handleBezier(const std::string&, const std::string&); + std::optional handleAnimation(const std::string&, const std::string&); + std::optional handleSource(const std::string&, const std::string&); + std::optional handleSubmap(const std::string&, const std::string&); + std::optional handleBlurLS(const std::string&, const std::string&); + std::optional handleBindWS(const std::string&, const std::string&); + std::optional handleEnv(const std::string&, const std::string&); + std::optional handlePlugin(const std::string&, const std::string&); + + std::string configCurrentPath; private: - std::deque configPaths; // stores all the config paths - std::unordered_map configModifyTimes; // stores modify times - std::vector> configDynamicVars; // stores dynamic vars declared by the user - std::unordered_map configValues; - std::unordered_map> deviceConfigs; // stores device configs + std::unique_ptr m_pConfig; - std::unordered_map animationConfig; // stores all the animations with their set values + std::deque configPaths; // stores all the config paths + std::unordered_map configModifyTimes; // stores modify times - std::string currentCategory = ""; // For storing the category of the current item + std::unordered_map animationConfig; // stores all the animations with their set values - std::string parseError = ""; // For storing a parse error to display later + std::string m_szCurrentSubmap = ""; // For storing the current keybind submap - std::string m_szCurrentSubmap = ""; // For storing the current keybind submap + std::vector execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty - std::vector execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty + std::vector m_vDeclaredPlugins; + std::vector pluginKeywords; + std::vector pluginVariables; - std::vector m_vDeclaredPlugins; - std::unordered_map>> pluginConfigs; // stores plugin configs - std::vector pluginKeywords; + bool isFirstLaunch = true; // For exec-once - bool isFirstLaunch = true; // For exec-once + std::deque m_dMonitorRules; + std::deque m_dWorkspaceRules; + std::deque m_dWindowRules; + std::deque m_dLayerRules; + std::deque m_dBlurLSNamespaces; - std::deque m_dMonitorRules; - std::deque m_dWorkspaceRules; - std::deque m_dWindowRules; - std::deque m_dLayerRules; - std::deque m_dBlurLSNamespaces; + bool firstExecDispatched = false; + bool m_bManualCrashInitiated = false; + std::deque firstExecRequests; - bool firstExecDispatched = false; - bool m_bManualCrashInitiated = false; - std::deque firstExecRequests; - - std::vector> environmentVariables; - - std::vector> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins + std::vector> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins // internal methods - void setDefaultVars(); - void setDefaultAnimationVars(); - void setDeviceDefaultVars(const std::string&); - void populateEnvironment(); - - void setAnimForChildren(SAnimationPropertyConfig* const); - void updateBlurredLS(const std::string&, const bool); - - void applyUserDefinedVars(std::string&, const size_t); - void loadConfigLoadVars(); - SConfigValue getConfigValueSafe(const std::string&); - SConfigValue getConfigValueSafeDevice(const std::string&, const std::string&, const std::string& fallback = ""); - void parseLine(std::string&); - void configSetValueSafe(const std::string&, const std::string&); - void handleDeviceConfig(const std::string&, const std::string&); - void handleRawExec(const std::string&, const std::string&); - void handleMonitor(const std::string&, const std::string&); - void handleBind(const std::string&, const std::string&); - void handleUnbind(const std::string&, const std::string&); - void handleWindowRule(const std::string&, const std::string&); - void handleLayerRule(const std::string&, const std::string&); - void handleWindowRuleV2(const std::string&, const std::string&); - void handleWorkspaceRules(const std::string&, const std::string&); - void handleBezier(const std::string&, const std::string&); - void handleAnimation(const std::string&, const std::string&); - void handleSource(const std::string&, const std::string&); - void handleSubmap(const std::string&, const std::string&); - void handleBlurLS(const std::string&, const std::string&); - void handleBindWS(const std::string&, const std::string&); - void handleEnv(const std::string&, const std::string&); - void handlePlugin(const std::string&, const std::string&); + void setAnimForChildren(SAnimationPropertyConfig* const); + void updateBlurredLS(const std::string&, const bool); + void setDefaultAnimationVars(); + std::optional resetHLConfig(); + std::optional verifyConfigExists(); + void postConfigReload(const Hyprlang::CParseResult& result); + void reload(); }; inline std::unique_ptr g_pConfigManager; diff --git a/src/config/defaultConfig.hpp b/src/config/defaultConfig.hpp index 1e149a7a..6d1d9751 100644 --- a/src/config/defaultConfig.hpp +++ b/src/config/defaultConfig.hpp @@ -3,11 +3,11 @@ #include inline const std::string AUTOCONFIG = R"#( -######################################################################################## -AUTOGENERATED HYPR CONFIG. -PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hypr.conf AND EDIT IT, -OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS. -######################################################################################## +# ####################################################################################### +# AUTOGENERATED HYPR CONFIG. +# PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hypr.conf AND EDIT IT, +# OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS. +# ####################################################################################### # # Please note not all available settings / options are set here. @@ -51,7 +51,7 @@ input { natural_scroll = no } - sensitivity = 0 # -1.0 - 1.0, 0 means no modification. + sensitivity = 0 # -1.0 to 1.0, 0 means no modification. } general { @@ -119,12 +119,13 @@ gestures { misc { # See https://wiki.hyprland.org/Configuring/Variables/ for more - force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers + force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers } # Example per-device config # See https://wiki.hyprland.org/Configuring/Keywords/#executing for more -device:epic-mouse-v1 { +device { + name = epic-mouse-v1 sensitivity = -0.5 } @@ -133,7 +134,7 @@ device:epic-mouse-v1 { # Example windowrule v2 # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # See https://wiki.hyprland.org/Configuring/Window-Rules/ for more -windowrulev2 = nomaximizerequest, class:.* # You'll probably like this. +windowrulev2 = suppressevent maximize, class:.* # You'll probably like this. # See https://wiki.hyprland.org/Configuring/Keywords/ for more diff --git a/src/debug/CrashReporter.cpp b/src/debug/CrashReporter.cpp index 65b31399..cf648191 100644 --- a/src/debug/CrashReporter.cpp +++ b/src/debug/CrashReporter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../plugins/PluginSystem.hpp" @@ -105,21 +106,38 @@ void CrashReporter::createAndSaveCrash(int sig) { const auto FPATH = std::filesystem::canonical("/proc/self/exe"); #endif + std::string addrs = ""; for (size_t i = 0; i < CALLSTACK.size(); ++i) { - finalCrashReport += std::format("\t#{} | {}\n", i, CALLSTACK[i].desc); + // convert in memory address to VMA address + Dl_info info; + struct link_map* linkMap; + dladdr1((void*)CALLSTACK[i].adr, &info, (void**)&linkMap, RTLD_DL_LINKMAP); + size_t vmaAddr = (size_t)CALLSTACK[i].adr - linkMap->l_addr; + addrs += std::format("0x{:x} ", vmaAddr); + } #ifdef __clang__ - const auto CMD = std::format("llvm-addr2line -e {} -f 0x{:x}", FPATH.c_str(), (uint64_t)CALLSTACK[i].adr); + const auto CMD = std::format("llvm-addr2line -e {} -Cf {}", FPATH.c_str(), addrs); #else - const auto CMD = std::format("addr2line -e {} -f 0x{:x}", FPATH.c_str(), (uint64_t)CALLSTACK[i].adr); + const auto CMD = std::format("addr2line -e {} -Cf {}", FPATH.c_str(), addrs); #endif - const auto ADDR2LINE = replaceInString(execAndGet(CMD.c_str()), "\n", "\n\t\t"); - finalCrashReport += "\t\t" + ADDR2LINE.substr(0, ADDR2LINE.length() - 2); + + const auto ADDR2LINE = execAndGet(CMD.c_str()); + + std::stringstream ssin(ADDR2LINE); + + for (size_t i = 0; i < CALLSTACK.size(); ++i) { + finalCrashReport += std::format("\t#{} | {}", i, CALLSTACK[i].desc); + std::string functionInfo; + std::string fileLineInfo; + std::getline(ssin, functionInfo); + std::getline(ssin, fileLineInfo); + finalCrashReport += std::format("\n\t\t{}\n\t\t{}\n", functionInfo, fileLineInfo); } finalCrashReport += "\n\nLog tail:\n"; - finalCrashReport += Debug::rollingLog; + finalCrashReport += Debug::rollingLog.substr(Debug::rollingLog.find("\n") + 1); const auto HOME = getenv("HOME"); const auto CACHE_HOME = getenv("XDG_CACHE_HOME"); @@ -128,21 +146,18 @@ void CrashReporter::createAndSaveCrash(int sig) { return; std::ofstream ofs; - std::string path; - if (!CACHE_HOME || std::string(CACHE_HOME).empty()) { - if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) - std::filesystem::create_directory(std::string(HOME) + "/.hyprland"); + std::string reportDir; - path = std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt"; - ofs.open(path, std::ios::trunc); + if (!CACHE_HOME || std::string(CACHE_HOME).empty()) + reportDir = std::string(HOME) + "/.cache/hyprland"; + else + reportDir = std::string(CACHE_HOME) + "/hyprland"; - } else { - if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland")) - std::filesystem::create_directory(std::string(CACHE_HOME) + "/hyprland"); + if (!std::filesystem::exists(reportDir)) + std::filesystem::create_directory(reportDir); + const auto path = reportDir + "/hyprlandCrashReport" + std::to_string(PID) + ".txt"; - path = std::string(CACHE_HOME) + "/hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt"; - ofs.open(path, std::ios::trunc); - } + ofs.open(path, std::ios::trunc); ofs << finalCrashReport; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index ab15ceae..8f29ae72 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -7,12 +7,13 @@ #include #include #include +#include #include #include -#include #include #include +#include static void trimTrailingComma(std::string& str) { if (!str.empty() && str.back() == ',') @@ -28,7 +29,7 @@ static std::string getWorkspaceNameFromSpecialID(const int workspaceID) { return workspace->m_szName; } -std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) { +std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); auto allMonitors = false; @@ -39,7 +40,7 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f allMonitors = true; std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { @@ -75,7 +76,7 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f "vrr": {}, "activelyTearing": {} }},)#", - m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szDescription), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), + m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspace, (m->activeWorkspace == -1 ? "" : escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName)), m->specialWorkspaceID, escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, @@ -97,7 +98,7 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f "workspace: {} ({})\n\treserved: {} " "{} {} {}\n\tscale: {:.2f}\n\ttransform: " "{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\n", - m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szDescription, + m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription, (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspace, (m->activeWorkspace == -1 ? "" : g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName), m->specialWorkspaceID, getWorkspaceNameFromSpecialID(m->specialWorkspaceID), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, @@ -109,8 +110,8 @@ std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat f return result; } -static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) { - const bool isJson = format == HyprCtl::FORMAT_JSON; +static std::string getGroupedData(CWindow* w, eHyprCtlOutputFormat format) { + const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON; if (!w->m_sGroupData.pNextWindow) return isJson ? "" : "0"; @@ -133,7 +134,7 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form return result.str(); } -static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) { +static std::string getWindowData(CWindow* w, eHyprCtlOutputFormat format) { auto getFocusHistoryID = [](CWindow* wnd) -> int { for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) { if (g_pCompositor->m_vWindowFocusHistory[i] == wnd) @@ -142,7 +143,7 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma return -1; }; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { return std::format( R"#({{ "address": "0x{:x}", @@ -198,9 +199,9 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma } } -std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& w : g_pCompositor->m_vWindows) { @@ -218,10 +219,10 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat format) { +static std::string getWorkspaceData(CWorkspace* w, eHyprCtlOutputFormat format) { const auto PLASTW = w->getLastFocusedWindow(); const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID); - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { return std::format(R"#({{ "id": {}, "name": "{}", @@ -242,14 +243,18 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat } } -static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprCtlOutputFormat format) { +static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputFormat format) { const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; }; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor)); const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : ""; const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : ""; - const std::string gapsIn = (bool)(r.gapsIn) ? std::format(",\n \"gapsIn\": {}", r.gapsIn.value()) : ""; - const std::string gapsOut = (bool)(r.gapsOut) ? std::format(",\n \"gapsOut\": {}", r.gapsOut.value()) : ""; + const std::string gapsIn = (bool)(r.gapsIn) ? + std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().top, r.gapsIn.value().right, r.gapsIn.value().bottom, r.gapsIn.value().left) : + ""; + const std::string gapsOut = (bool)(r.gapsOut) ? + std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().top, r.gapsOut.value().right, r.gapsOut.value().bottom, r.gapsOut.value().left) : + ""; const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : ""; const std::string border = (bool)(r.border) ? std::format(",\n \"border\": {}", boolToString(r.border.value())) : ""; const std::string rounding = (bool)(r.rounding) ? std::format(",\n \"rounding\": {}", boolToString(r.rounding.value())) : ""; @@ -266,8 +271,12 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprC const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "" : escapeJSONStrings(r.monitor)); const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : ""); const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : ""); - const std::string gapsIn = std::format("\tgapsIn: {}\n", (bool)(r.gapsIn) ? std::to_string(r.gapsIn.value()) : ""); - const std::string gapsOut = std::format("\tgapsOut: {}\n", (bool)(r.gapsOut) ? std::to_string(r.gapsOut.value()) : ""); + const std::string gapsIn = (bool)(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().top), std::to_string(r.gapsIn.value().right), + std::to_string(r.gapsIn.value().bottom), std::to_string(r.gapsIn.value().left)) : + std::format("\tgapsIn: \n"); + const std::string gapsOut = (bool)(r.gapsOut) ? std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().top), std::to_string(r.gapsOut.value().right), + std::to_string(r.gapsOut.value().bottom), std::to_string(r.gapsOut.value().left)) : + std::format("\tgapsOut: \n"); const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : ""); const std::string border = std::format("\tborder: {}\n", (bool)(r.border) ? boolToString(r.border.value()) : ""); const std::string rounding = std::format("\trounding: {}\n", (bool)(r.rounding) ? boolToString(r.rounding.value()) : ""); @@ -280,7 +289,7 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprC return result; } } -std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) { if (!g_pCompositor->m_pLastMonitor) return "unsafe state"; @@ -293,10 +302,10 @@ std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) { return getWorkspaceData(w, format); } -std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& w : g_pCompositor->m_vWorkspaces) { result += getWorkspaceData(w.get(), format); @@ -314,9 +323,9 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& r : g_pConfigManager->getAllWorkspaceRules()) { result += getWorkspaceRuleData(r, format); @@ -334,24 +343,24 @@ std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) { const auto PWINDOW = g_pCompositor->m_pLastWindow; if (!g_pCompositor->windowValidMapped(PWINDOW)) - return format == HyprCtl::FORMAT_JSON ? "{}" : "Invalid"; + return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid"; auto result = getWindowData(PWINDOW, format); - if (format == HyprCtl::FORMAT_JSON) + if (format == eHyprCtlOutputFormat::FORMAT_JSON) result.pop_back(); return result; } -std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string layersRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "{\n"; for (auto& mon : g_pCompositor->m_vMonitors) { @@ -422,9 +431,9 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& m : g_pLayoutManager->getAllLayoutNames()) { @@ -444,10 +453,10 @@ std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "{\n"; result += "\"mice\": [\n"; @@ -603,9 +612,9 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string animationsRequest(eHyprCtlOutputFormat format, std::string request) { std::string ret = ""; - if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { + if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { ret += "animations:\n"; for (auto& ac : g_pConfigManager->getAnimationConfig()) { @@ -656,10 +665,10 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) { return ret; } -std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "[\n\"log\":\""; result += escapeJSONStrings(Debug::rollingLog); result += "\"]"; @@ -670,10 +679,10 @@ std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) { return result; } -std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string request) { std::string ret = ""; const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts(); - if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { + if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { for (auto& sh : SHORTCUTS) ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description); } else { @@ -693,9 +702,9 @@ std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) { return ret; } -std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) { std::string ret = ""; - if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { + if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { for (auto& kb : g_pKeybindManager->m_lKeybinds) { ret += "bind"; if (kb.locked) @@ -741,12 +750,12 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) { return ret; } -std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string versionRequest(eHyprCtlOutputFormat format, std::string request) { auto commitMsg = removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE); std::replace(commitMsg.begin(), commitMsg.end(), '#', ' '); - if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { + if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg + ").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n"; @@ -793,7 +802,40 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) { return ""; // make the compiler happy } -std::string dispatchRequest(std::string in) { +std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) { + std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, ""); + + result += "\n\nSystem Information:\n"; + + struct utsname unameInfo; + + uname(&unameInfo); + + result += "System name: " + std::string{unameInfo.sysname} + "\n"; + result += "Node name: " + std::string{unameInfo.nodename} + "\n"; + result += "Release: " + std::string{unameInfo.release} + "\n"; + result += "Version: " + std::string{unameInfo.version} + "\n"; + + result += "\n\n"; + +#if defined(__DragonFly__) || defined(__FreeBSD__) + const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga"); +#else + const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA"); +#endif + result += "GPU information: \n" + GPUINFO + "\n\n"; + + result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n"; + + result += "plugins:\n"; + for (auto& pl : g_pPluginSystem->getAllPlugins()) { + result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version); + } + + return result; +} + +std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) { // get rid of the dispatch keyword in = in.substr(in.find_first_of(' ') + 1); @@ -814,7 +856,7 @@ std::string dispatchRequest(std::string in) { return "ok"; } -std::string dispatchKeyword(std::string in) { +std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) { // get rid of the keyword keyword in = in.substr(in.find_first_of(' ') + 1); @@ -822,32 +864,35 @@ std::string dispatchKeyword(std::string in) { const auto VALUE = in.substr(in.find_first_of(' ') + 1); - std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE, true); + std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE); - if (COMMAND == "monitor") + // if we are executing a dynamic source we have to reload everything, so every if will have a check for source. + if (COMMAND == "monitor" || COMMAND == "source") g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords - if (COMMAND.contains("input") || COMMAND.contains("device:")) { + if (COMMAND.contains("input") || COMMAND.contains("device") || COMMAND == "source") { g_pInputManager->setKeyboardLayout(); // update kb layout g_pInputManager->setPointerConfigs(); // update mouse cfgs g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs g_pInputManager->setTabletConfigs(); // update tablets } - if (COMMAND.contains("general:layout")) - g_pLayoutManager->switchToLayout(g_pConfigManager->getString("general:layout")); // update layout + static auto* const PLAYOUT = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("general:layout"); - if (COMMAND.contains("decoration:screen_shader")) + if (COMMAND.contains("general:layout")) + g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout + + if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source") g_pHyprOpenGL->m_bReloadScreenShader = true; - if (COMMAND.contains("blur")) { + if (COMMAND.contains("blur") || COMMAND == "source") { for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) { rd.blurFBDirty = true; } } // decorations will probably need a repaint - if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("cursor_zoom_factor")) { + if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("cursor_zoom_factor") || COMMAND == "source") { for (auto& m : g_pCompositor->m_vMonitors) { g_pHyprRenderer->damageMonitor(m.get()); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); @@ -862,7 +907,7 @@ std::string dispatchKeyword(std::string in) { return retval; } -std::string reloadRequest(const std::string& request) { +std::string reloadRequest(eHyprCtlOutputFormat format, std::string request) { const auto REQMODE = request.substr(request.find_last_of(' ') + 1); @@ -877,20 +922,20 @@ std::string reloadRequest(const std::string& request) { return "ok"; } -std::string killRequest() { +std::string killRequest(eHyprCtlOutputFormat format, std::string request) { g_pInputManager->setClickMode(CLICKMODE_KILL); return "ok"; } -std::string splashRequest() { +std::string splashRequest(eHyprCtlOutputFormat format, std::string request) { return g_pCompositor->m_szCurrentSplash; } -std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) { +std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) { const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor(); - if (format == HyprCtl::FORMAT_NORMAL) { + if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y); } else { return std::format(R"#( @@ -905,9 +950,7 @@ std::string cursorPosRequest(HyprCtl::eHyprCtlOutputFormat format) { return "error"; } -std::string getReply(std::string); - -std::string dispatchBatch(std::string request) { +std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) { // split by ; request = request.substr(9); @@ -930,8 +973,8 @@ std::string dispatchBatch(std::string request) { nextItem(); - while (curitem != "") { - reply += getReply(curitem); + while (curitem != "" || request != "") { + reply += g_pHyprCtl->getReply(curitem); nextItem(); } @@ -939,7 +982,7 @@ std::string dispatchBatch(std::string request) { return reply; } -std::string dispatchSetCursor(std::string request) { +std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); const auto SIZESTR = vars[vars.size() - 1]; @@ -971,7 +1014,7 @@ std::string dispatchSetCursor(std::string request) { return "ok"; } -std::string switchXKBLayoutRequest(const std::string& request) { +std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); const auto KB = vars[1]; @@ -1017,7 +1060,7 @@ std::string switchXKBLayoutRequest(const std::string& request) { return "ok"; } -std::string dispatchSeterror(std::string request) { +std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); std::string errorMessage = ""; @@ -1046,13 +1089,14 @@ std::string dispatchSeterror(std::string request) { return "ok"; } -std::string dispatchSetProp(std::string request) { +std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); if (vars.size() < 4) return "not enough args"; - const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow; + const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); if (!PWINDOW) return "window not found"; @@ -1060,6 +1104,8 @@ std::string dispatchSetProp(std::string request) { const auto PROP = vars[2]; const auto VAL = vars[3]; + auto noFocus = PWINDOW->m_sAdditionalConfigData.noFocus; + bool lock = false; if (vars.size() > 4) { @@ -1089,6 +1135,8 @@ std::string dispatchSetProp(std::string request) { PWINDOW->m_sAdditionalConfigData.forceNoShadow.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "forcenodim") { PWINDOW->m_sAdditionalConfigData.forceNoDim.forceSetIgnoreLocked(configStringToInt(VAL), lock); + } else if (PROP == "nofocus") { + PWINDOW->m_sAdditionalConfigData.noFocus.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "windowdancecompat") { PWINDOW->m_sAdditionalConfigData.windowDanceCompat.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "nomaxsize") { @@ -1124,13 +1172,19 @@ std::string dispatchSetProp(std::string request) { g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + if (!(PWINDOW->m_sAdditionalConfigData.noFocus.toUnderlying() == noFocus.toUnderlying())) { + g_pCompositor->focusWindow(nullptr); + g_pCompositor->focusWindow(PWINDOW); + g_pCompositor->focusWindow(PLASTWINDOW); + } + for (auto& m : g_pCompositor->m_vMonitors) g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); return "ok"; } -std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat format) { +std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) { std::string curitem = ""; auto nextItem = [&]() { @@ -1150,31 +1204,43 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat nextItem(); nextItem(); - const auto PCFGOPT = g_pConfigManager->getConfigValuePtrSafe(curitem); + const auto VAR = g_pConfigManager->getHyprlangConfigValuePtr(curitem); - if (!PCFGOPT) + if (!VAR) return "no such option"; - if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) - return std::format("option {}\n\tint: {}\n\tfloat: {:.5f}\n\tstr: \"{}\"\n\tdata: {:x}\n\tset: {}", curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue, - (uintptr_t)PCFGOPT->data.get(), PCFGOPT->set); - else { - return std::format( - R"#( -{{ - "option": "{}", - "int": {}, - "float": {:.5f}, - "str": "{}", - "data": "0x{:x}", - "set": {} -}} -)#", - curitem, PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue, (uintptr_t)PCFGOPT->data.get(), PCFGOPT->set); + const auto VAL = VAR->getValue(); + const auto TYPE = std::type_index(VAL.type()); + + if (format == FORMAT_NORMAL) { + if (TYPE == typeid(Hyprlang::INT)) + return std::format("int: {}\nset: {}", std::any_cast(VAL), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::FLOAT)) + return std::format("float: {:2f}\nset: {}", std::any_cast(VAL), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::VEC2)) + return std::format("vec2: [{}, {}]\nset: {}", std::any_cast(VAL).x, std::any_cast(VAL).y, VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::STRING)) + return std::format("str: {}\nset: {}", std::any_cast(VAL), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::CUSTOMTYPE*)) + return std::format("custom type at: {:x}\nset: {}", (uintptr_t)std::any_cast(VAL), VAR->m_bSetByUser); + } else { + if (TYPE == typeid(Hyprlang::INT)) + return std::format("{{\"option\": \"{}\", \"int\": {}, \"set\": {} }}", curitem, std::any_cast(VAL), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::FLOAT)) + return std::format("{{\"option\": \"{}\", \"float\": {:2f}, \"set\": {} }}", curitem, std::any_cast(VAL), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::VEC2)) + return std::format("{{\"option\": \"{}\", \"vec2\": [{},{}], \"set\": {} }}", curitem, std::any_cast(VAL).x, std::any_cast(VAL).y, + VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::STRING)) + return std::format("{{\"option\": \"{}\", \"str\": \"{}\", \"set\": {} }}", curitem, escapeJSONStrings(std::any_cast(VAL)), VAR->m_bSetByUser); + else if (TYPE == typeid(Hyprlang::CUSTOMTYPE*)) + return std::format("{{\"option\": \"{}\", \"custom\": \"{:x}\", \"set\": {} }}", curitem, (uintptr_t)std::any_cast(VAL), VAR->m_bSetByUser); } + + return "invalid type (internal error)"; } -std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) { +std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); @@ -1182,7 +1248,7 @@ std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat return "none"; std::string result = ""; - if (format == HyprCtl::FORMAT_JSON) { + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; for (auto& wd : PWINDOW->m_dWindowDecorations) { result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},"; @@ -1231,7 +1297,7 @@ void createOutputIter(wlr_backend* backend, void* data) { } } -std::string dispatchOutput(std::string request) { +std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) { std::string curitem = ""; auto nextItem = [&]() { @@ -1280,7 +1346,7 @@ std::string dispatchOutput(std::string request) { return "ok"; } -std::string dispatchPlugin(std::string request) { +std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); if (vars.size() < 2) @@ -1323,7 +1389,7 @@ std::string dispatchPlugin(std::string request) { return "ok"; } -std::string dispatchNotify(std::string request) { +std::string dispatchNotify(eHyprCtlOutputFormat format, std::string request) { CVarList vars(request, 0, ' '); if (vars.size() < 5) @@ -1364,92 +1430,137 @@ std::string dispatchNotify(std::string request) { return "ok"; } -std::string getReply(std::string request) { - auto format = HyprCtl::FORMAT_NORMAL; +CHyprCtl::CHyprCtl() { + registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest}); + registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest}); + registerCommand(SHyprCtlCommand{"activeworkspace", true, activeWorkspaceRequest}); + registerCommand(SHyprCtlCommand{"clients", true, clientsRequest}); + registerCommand(SHyprCtlCommand{"kill", true, killRequest}); + registerCommand(SHyprCtlCommand{"activewindow", true, activeWindowRequest}); + registerCommand(SHyprCtlCommand{"layers", true, layersRequest}); + registerCommand(SHyprCtlCommand{"version", true, versionRequest}); + registerCommand(SHyprCtlCommand{"devices", true, devicesRequest}); + registerCommand(SHyprCtlCommand{"splash", true, splashRequest}); + registerCommand(SHyprCtlCommand{"cursorpos", true, cursorPosRequest}); + registerCommand(SHyprCtlCommand{"binds", true, bindsRequest}); + registerCommand(SHyprCtlCommand{"globalshortcuts", true, globalShortcutsRequest}); + registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest}); + registerCommand(SHyprCtlCommand{"animations", true, animationsRequest}); + registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest}); + registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest}); + + registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); + registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); + registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); + registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify}); + registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp}); + registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror}); + registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest}); + registerCommand(SHyprCtlCommand{"output", false, dispatchOutput}); + registerCommand(SHyprCtlCommand{"dispatch", false, dispatchRequest}); + registerCommand(SHyprCtlCommand{"keyword", false, dispatchKeyword}); + registerCommand(SHyprCtlCommand{"setcursor", false, dispatchSetCursor}); + registerCommand(SHyprCtlCommand{"getoption", false, dispatchGetOption}); + registerCommand(SHyprCtlCommand{"decorations", false, decorationRequest}); + registerCommand(SHyprCtlCommand{"[[BATCH]]", false, dispatchBatch}); + + startHyprCtlSocket(); +} + +std::shared_ptr CHyprCtl::registerCommand(SHyprCtlCommand cmd) { + return m_vCommands.emplace_back(std::make_shared(cmd)); +} + +void CHyprCtl::unregisterCommand(const std::shared_ptr& cmd) { + std::erase(m_vCommands, cmd); +} + +std::string CHyprCtl::getReply(std::string request) { + auto format = eHyprCtlOutputFormat::FORMAT_NORMAL; + bool reloadAll = false; // process flags for non-batch requests - if (!request.contains("[[BATCH]]") && request.contains("/")) { + if (!request.starts_with("[[BATCH]]") && request.contains("/")) { long unsigned int sepIndex = 0; for (const auto& c : request) { if (c == '/') { // stop at separator break; } + // after whitespace assume the first word as a keyword, + // so its value can have slashes (e.g., a path) + if (c == ' ') { + sepIndex = request.size(); + break; + } + sepIndex++; if (c == 'j') - format = HyprCtl::FORMAT_JSON; + format = eHyprCtlOutputFormat::FORMAT_JSON; + if (c == 'r') + reloadAll = true; } if (sepIndex < request.size()) request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string } - if (request.starts_with("monitors")) - return monitorsRequest(request, format); - else if (request == "workspaces") - return workspacesRequest(format); - else if (request == "workspacerules") - return workspaceRulesRequest(format); - else if (request == "activeworkspace") - return activeWorkspaceRequest(format); - else if (request == "clients") - return clientsRequest(format); - else if (request == "kill") - return killRequest(); - else if (request == "activewindow") - return activeWindowRequest(format); - else if (request == "layers") - return layersRequest(format); - else if (request == "version") - return versionRequest(format); - else if (request.starts_with("reload")) - return reloadRequest(request); - else if (request == "devices") - return devicesRequest(format); - else if (request == "splash") - return splashRequest(); - else if (request == "cursorpos") - return cursorPosRequest(format); - else if (request == "binds") - return bindsRequest(format); - else if (request == "globalshortcuts") - return globalShortcutsRequest(format); - else if (request == "animations") - return animationsRequest(format); - else if (request == "rollinglog") - return rollinglogRequest(format); - else if (request == "layouts") - return layoutsRequest(format); - else if (request.starts_with("plugin")) - return dispatchPlugin(request); - else if (request.starts_with("notify")) - return dispatchNotify(request); - else if (request.starts_with("setprop")) - return dispatchSetProp(request); - else if (request.starts_with("seterror")) - return dispatchSeterror(request); - else if (request.starts_with("switchxkblayout")) - return switchXKBLayoutRequest(request); - else if (request.starts_with("output")) - return dispatchOutput(request); - else if (request.starts_with("dispatch")) - return dispatchRequest(request); - else if (request.starts_with("keyword")) - return dispatchKeyword(request); - else if (request.starts_with("setcursor")) - return dispatchSetCursor(request); - else if (request.starts_with("getoption")) - return dispatchGetOption(request, format); - else if (request.starts_with("decorations")) - return decorationRequest(request, format); - else if (request.starts_with("[[BATCH]]")) - return dispatchBatch(request); + std::string result = ""; - return "unknown request"; + // parse exact cmds first, then non-exact. + for (auto& cmd : m_vCommands) { + if (!cmd->exact) + continue; + + if (cmd->name == request) { + result = cmd->fn(format, request); + break; + } + } + + if (result.empty()) + for (auto& cmd : m_vCommands) { + if (cmd->exact) + continue; + + if (request.starts_with(cmd->name)) { + result = cmd->fn(format, request); + break; + } + } + + if (result.empty()) + return "unknown request"; + + if (reloadAll) { + g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords + + g_pInputManager->setKeyboardLayout(); // update kb layout + g_pInputManager->setPointerConfigs(); // update mouse cfgs + g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs + g_pInputManager->setTabletConfigs(); // update tablets + + static auto* const PLAYOUT = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("general:layout"); + + g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout + + g_pHyprOpenGL->m_bReloadScreenShader = true; + + for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) { + rd.blurFBDirty = true; + } + + for (auto& m : g_pCompositor->m_vMonitors) { + g_pHyprRenderer->damageMonitor(m.get()); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); + } + } + + return result; } -std::string HyprCtl::makeDynamicCall(const std::string& input) { +std::string CHyprCtl::makeDynamicCall(const std::string& input) { return getReply(input); } @@ -1460,7 +1571,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) { sockaddr_in clientAddress; socklen_t clientSize = sizeof(clientAddress); - const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC); + const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC); std::array readBuffer; @@ -1490,7 +1601,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) { std::string reply = ""; try { - reply = getReply(request); + reply = g_pHyprCtl->getReply(request); } catch (std::exception& e) { Debug::log(ERR, "Error in request: {}", e.what()); reply = "Err: " + std::string(e.what()); @@ -1500,18 +1611,17 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) { close(ACCEPTEDCONNECTION); - if (g_pConfigManager->m_bWantsMonitorReload) { + if (g_pConfigManager->m_bWantsMonitorReload) g_pConfigManager->ensureMonitorStatus(); - } return 0; } -void HyprCtl::startHyprCtlSocket() { +void CHyprCtl::startHyprCtlSocket() { - iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (iSocketFD < 0) { + if (m_iSocketFD < 0) { Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work."); return; } @@ -1522,15 +1632,15 @@ void HyprCtl::startHyprCtlSocket() { strcpy(SERVERADDRESS.sun_path, socketPath.c_str()); - if (bind(iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) { + if (bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) { Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work."); return; } // 10 max queued. - listen(iSocketFD, 10); + listen(m_iSocketFD, 10); Debug::log(LOG, "Hypr socket started at {}", socketPath); - wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr); + wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr); } diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp index 00cf7c5b..339acd1b 100644 --- a/src/debug/HyprCtl.hpp +++ b/src/debug/HyprCtl.hpp @@ -3,24 +3,23 @@ #include "../Compositor.hpp" #include #include "../helpers/MiscFunctions.hpp" +#include -namespace HyprCtl { - void startHyprCtlSocket(); - std::string makeDynamicCall(const std::string& input); +class CHyprCtl { + public: + CHyprCtl(); - // very simple thread-safe request method - inline bool requestMade = false; - inline bool requestReady = false; - inline std::string request = ""; + std::string makeDynamicCall(const std::string& input); + std::shared_ptr registerCommand(SHyprCtlCommand cmd); + void unregisterCommand(const std::shared_ptr& cmd); + std::string getReply(std::string); - inline std::ifstream requestStream; + int m_iSocketFD = -1; - inline wl_event_source* hyprCtlTickSource = nullptr; + private: + void startHyprCtlSocket(); - inline int iSocketFD = -1; + std::vector> m_vCommands; +}; - enum eHyprCtlOutputFormat { - FORMAT_NORMAL = 0, - FORMAT_JSON - }; -}; \ No newline at end of file +inline std::unique_ptr g_pHyprCtl; \ No newline at end of file diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index 12f7a575..2968134b 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -226,4 +226,8 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) { CBox pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}; g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f); +} + +bool CHyprNotificationOverlay::hasAny() { + return !m_dNotifications.empty(); } \ No newline at end of file diff --git a/src/debug/HyprNotificationOverlay.hpp b/src/debug/HyprNotificationOverlay.hpp index 84e61f80..7a883f15 100644 --- a/src/debug/HyprNotificationOverlay.hpp +++ b/src/debug/HyprNotificationOverlay.hpp @@ -41,6 +41,7 @@ class CHyprNotificationOverlay { void draw(CMonitor* pMonitor); void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE); + bool hasAny(); private: CBox drawNotifications(CMonitor* pMonitor); diff --git a/src/debug/Log.cpp b/src/debug/Log.cpp index 404904a2..66c430d5 100644 --- a/src/debug/Log.cpp +++ b/src/debug/Log.cpp @@ -22,7 +22,7 @@ void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) { rollingLog += output + "\n"; - if (!disableLogs || !*disableLogs) { + if (!disableLogs || !**disableLogs) { std::ofstream ofs; ofs.open(logFile, std::ios::out | std::ios::app); ofs << "[wlr] " << output << "\n"; diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp index 3d2ed48c..a43d6fee 100644 --- a/src/debug/Log.hpp +++ b/src/debug/Log.hpp @@ -21,16 +21,16 @@ enum LogLevel { }; namespace Debug { - inline std::string logFile; - inline int64_t* disableLogs = nullptr; - inline int64_t* disableTime = nullptr; - inline bool disableStdout = false; - inline bool trace = false; - inline bool shuttingDown = false; + inline std::string logFile; + inline int64_t* const* disableLogs = nullptr; + inline int64_t* const* disableTime = nullptr; + inline bool disableStdout = false; + inline bool trace = false; + inline bool shuttingDown = false; - inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log + inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log - void init(const std::string& IS); + void init(const std::string& IS); template void log(LogLevel level, std::format_string fmt, Args&&... args) { if (level == TRACE && !trace) @@ -52,7 +52,7 @@ namespace Debug { } // print date and time to the ofs - if (disableTime && !*disableTime) { + if (disableTime && !**disableTime) { #ifndef _LIBCPP_VERSION logMsg += std::format("[{:%T}] ", std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}); #else @@ -73,7 +73,7 @@ namespace Debug { if (rollingLog.size() > ROLLING_LOG_SIZE) rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE); - if (!disableLogs || !*disableLogs) { + if (!disableLogs || !**disableLogs) { // log to a file std::ofstream ofs; ofs.open(logFile, std::ios::out | std::ios::app); diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 777bdf9f..8b737f32 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -174,4 +174,7 @@ namespace Events { // Tearing hints LISTENER(newTearingHint); + + // Shortcut inhibitor + LISTENER(newShortcutInhibitor); }; diff --git a/src/events/Layers.cpp b/src/events/Layers.cpp index d6e21d9d..9ef9b025 100644 --- a/src/events/Layers.cpp +++ b/src/events/Layers.cpp @@ -142,6 +142,9 @@ void Events::listener_mapLayerSurface(void* owner, void* data) { wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output); + if (layersurface->layerSurface->current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + g_pInputManager->m_dExclusiveLSes.push_back(layersurface); + const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && // don't focus if constrained (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint); @@ -183,6 +186,11 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) { g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")}); EMIT_HOOK_EVENT("closeLayer", layersurface); + std::erase(g_pInputManager->m_dExclusiveLSes, layersurface); + + if (!g_pInputManager->m_dExclusiveLSes.empty()) + g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface); + if (!g_pCompositor->getMonitorFromID(layersurface->monitorID) || g_pCompositor->m_bUnsafeState) { Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 91a0ebda..35721832 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -128,6 +128,8 @@ void Events::listener_destroyDrag(void* owner, void* data) { g_pInputManager->m_sDrag.drag = nullptr; g_pInputManager->m_sDrag.dragIcon = nullptr; g_pInputManager->m_sDrag.hyprListener_destroy.removeCallback(); + + g_pCompositor->focusWindow(g_pCompositor->m_pLastWindow, g_pCompositor->m_pLastWindow ? g_pXWaylandManager->getWindowSurface(g_pCompositor->m_pLastWindow) : nullptr); } void Events::listener_mapDragIcon(void* owner, void* data) { @@ -175,11 +177,17 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) { void Events::listener_powerMgrSetMode(wl_listener* listener, void* data) { Debug::log(LOG, "PowerMgr set mode!"); - const auto EVENT = (wlr_output_power_v1_set_mode_event*)data; + const auto EVENT = (wlr_output_power_v1_set_mode_event*)data; + const auto PMONITOR = g_pCompositor->getMonitorFromOutput(EVENT->output); - wlr_output_enable(EVENT->output, EVENT->mode == 1); + if (!PMONITOR) { + Debug::log(ERR, "Invalid powerMgrSetMode output"); + return; + } - if (!wlr_output_commit(EVENT->output)) + wlr_output_state_set_enabled(PMONITOR->state.wlr(), EVENT->mode == 1); + + if (!PMONITOR->state.commit()) Debug::log(ERR, "Couldn't set power mode"); } @@ -225,16 +233,7 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) { } void Events::listener_newTearingHint(wl_listener* listener, void* data) { - const auto TCTL = (wlr_tearing_control_v1*)data; - - const auto PWINDOW = g_pCompositor->getWindowFromSurface(TCTL->surface); - - if (!PWINDOW) { - Debug::log(ERR, "Tearing hint {} was attached to an unknown surface", (uintptr_t)data); - return; - } - - Debug::log(LOG, "New tearing hint for window {} at {}", PWINDOW, (uintptr_t)data); + Debug::log(LOG, "New tearing hint at {:x}", (uintptr_t)data); const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique()).get(); NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data; @@ -242,7 +241,7 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) { NEWCTRL->hyprListener_destroy.initCallback( &NEWCTRL->pWlrHint->events.destroy, [&](void* owner, void* data) { - Debug::log(LOG, "Destroyed {} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint); + Debug::log(LOG, "Destroyed {:x} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint); std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; }); }, @@ -256,10 +255,27 @@ void Events::listener_newTearingHint(wl_listener* listener, void* data) { const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface); if (PWINDOW) { - PWINDOW->m_bTearingHint = TEARINGHINT->pWlrHint->hint; + PWINDOW->m_bTearingHint = (bool)TEARINGHINT->pWlrHint->current; - Debug::log(LOG, "Hint {} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->hint); + Debug::log(LOG, "Hint {:x} (window {}) set tearing hint to {}", (uintptr_t)TEARINGHINT->pWlrHint, PWINDOW, (uint32_t)TEARINGHINT->pWlrHint->current); } }, NEWCTRL, "TearingController"); } + +void Events::listener_newShortcutInhibitor(wl_listener* listener, void* data) { + const auto INHIBITOR = (wlr_keyboard_shortcuts_inhibitor_v1*)data; + + const auto PINH = &g_pKeybindManager->m_lShortcutInhibitors.emplace_back(); + PINH->hyprListener_destroy.initCallback( + &INHIBITOR->events.destroy, + [](void* owner, void* data) { + const auto OWNER = (SShortcutInhibitor*)owner; + g_pKeybindManager->m_lShortcutInhibitors.remove(*OWNER); + }, + PINH, "ShortcutInhibitor"); + + PINH->pWlrInhibitor = INHIBITOR; + + Debug::log(LOG, "New shortcut inhibitor for surface {:x}", (uintptr_t)INHIBITOR->surface); +} diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index 13f3f829..01ea24f8 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -109,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { for (auto& w : g_pCompositor->m_vWindows) { if (w->m_iMonitorID == PNEWMONITOR->ID) { w->m_iLastSurfaceMonitorID = -1; - w->updateSurfaceOutputs(); + w->updateSurfaceScaleTransformDetails(); } } } @@ -147,12 +147,12 @@ void Events::listener_monitorFrame(void* owner, void* data) { PMONITOR->tearingState.frameScheduledWhileBusy = false; } - static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue; - static auto* const PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue; + static auto* const PENABLERAT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time"); + static auto* const PRATSAFE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone"); PMONITOR->lastPresentationTimer.reset(); - if (*PENABLERAT && !PMONITOR->tearingState.nextRenderTorn) { + if (**PENABLERAT && !PMONITOR->tearingState.nextRenderTorn) { if (!PMONITOR->RATScheduled) { // render g_pHyprRenderer->renderMonitor(PMONITOR); @@ -162,14 +162,14 @@ void Events::listener_monitorFrame(void* owner, void* data) { const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(PMONITOR); - if (max + *PRATSAFE > 1000.0 / PMONITOR->refreshRate) + if (max + **PRATSAFE > 1000.0 / PMONITOR->refreshRate) return; const auto MSLEFT = 1000.0 / PMONITOR->refreshRate - PMONITOR->lastPresentationTimer.getMillis(); PMONITOR->RATScheduled = true; - const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE); + const auto ESTRENDERTIME = std::ceil(avg + **PRATSAFE); const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME); if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1) @@ -212,7 +212,17 @@ void Events::listener_monitorStateRequest(void* owner, void* data) { const auto PMONITOR = (CMonitor*)owner; const auto E = (wlr_output_event_request_state*)data; - wlr_output_commit_state(PMONITOR->output, E->state); + if (!PMONITOR->createdByUser) + return; + + const auto SIZE = E->state->mode ? Vector2D{E->state->mode->width, E->state->mode->height} : Vector2D{E->state->custom_mode.width, E->state->custom_mode.height}; + + PMONITOR->forceSize = SIZE; + + SMonitorRule rule = PMONITOR->activeMonitorRule; + rule.resolution = SIZE; + + g_pHyprRenderer->applyMonitorRule(PMONITOR, &rule); } void Events::listener_monitorDamage(void* owner, void* data) { diff --git a/src/events/Popups.cpp b/src/events/Popups.cpp index bcdb1808..41707b6a 100644 --- a/src/events/Popups.cpp +++ b/src/events/Popups.cpp @@ -55,7 +55,7 @@ void addPopupGlobalCoords(void* pPopup, int* x, int* y) { void createNewPopup(wlr_xdg_popup* popup, SXDGPopup* pHyprPopup) { pHyprPopup->popup = popup; - pHyprPopup->hyprListener_destroyPopupXDG.initCallback(&popup->base->events.destroy, &Events::listener_destroyPopupXDG, pHyprPopup, "HyprPopup"); + pHyprPopup->hyprListener_destroyPopupXDG.initCallback(&popup->events.destroy, &Events::listener_destroyPopupXDG, pHyprPopup, "HyprPopup"); pHyprPopup->hyprListener_mapPopupXDG.initCallback(&popup->base->surface->events.map, &Events::listener_mapPopupXDG, pHyprPopup, "HyprPopup"); pHyprPopup->hyprListener_unmapPopupXDG.initCallback(&popup->base->surface->events.unmap, &Events::listener_unmapPopupXDG, pHyprPopup, "HyprPopup"); pHyprPopup->hyprListener_newPopupFromPopupXDG.initCallback(&popup->base->events.new_popup, &Events::listener_newPopupFromPopupXDG, pHyprPopup, "HyprPopup"); @@ -228,6 +228,11 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) { void Events::listener_commitPopupXDG(void* owner, void* data) { SXDGPopup* PPOPUP = (SXDGPopup*)owner; + if (PPOPUP->popup->base->initial_commit) { + wlr_xdg_surface_schedule_configure(PPOPUP->popup->base); + return; + } + if (g_pCompositor->windowValidMapped(PPOPUP->parentWindow)) { PPOPUP->lx = PPOPUP->parentWindow->m_vRealPosition.vec().x; PPOPUP->ly = PPOPUP->parentWindow->m_vRealPosition.vec().y; diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index ce5103ee..81c5c800 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -40,17 +40,16 @@ void setAnimToMove(void* data) { void Events::listener_mapWindow(void* owner, void* data) { CWindow* PWINDOW = (CWindow*)owner; - static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue; - static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue; - static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue; - static auto* const PSWALLOW = &g_pConfigManager->getConfigValuePtr("misc:enable_swallow")->intValue; - static auto* const PSWALLOWREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_regex")->strValue; - static auto* const PSWALLOWEXREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_exception_regex")->strValue; - static auto* const PNEWTAKESOVERFS = &g_pConfigManager->getConfigValuePtr("misc:new_window_takes_over_fullscreen")->intValue; + static auto* const PINACTIVEALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity"); + static auto* const PACTIVEALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:active_opacity"); + static auto* const PDIMSTRENGTH = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_strength"); + static auto* const PSWALLOW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:enable_swallow"); + static auto* const PSWALLOWREGEX = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("misc:swallow_regex"); + static auto* const PSWALLOWEXREGEX = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("misc:swallow_exception_regex"); + static auto* const PNEWTAKESOVERFS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:new_window_takes_over_fullscreen"); auto PMONITOR = g_pCompositor->m_pLastMonitor; - const auto PWORKSPACE = - PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace); + auto PWORKSPACE = PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace); PWINDOW->m_iMonitorID = PMONITOR->ID; PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace; PWINDOW->m_bIsMapped = true; @@ -101,7 +100,7 @@ void Events::listener_mapWindow(void* owner, void* data) { } // window rules - const auto WINDOWRULES = g_pConfigManager->getMatchingRules(PWINDOW); + const auto WINDOWRULES = g_pConfigManager->getMatchingRules(PWINDOW, false); std::string requestedWorkspace = ""; bool workspaceSilent = false; bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || @@ -173,13 +172,23 @@ void Events::listener_mapWindow(void* owner, void* data) { } else if (r.szRule.starts_with("pseudo")) { PWINDOW->m_bIsPseudotiled = true; } else if (r.szRule.starts_with("nofocus")) { - PWINDOW->m_bNoFocus = true; + PWINDOW->m_sAdditionalConfigData.noFocus = true; } else if (r.szRule.starts_with("noinitialfocus")) { PWINDOW->m_bNoInitialFocus = true; - } else if (r.szRule.starts_with("nofullscreenrequest")) { - PWINDOW->m_bNoFullscreenRequest = true; - } else if (r.szRule.starts_with("nomaximizerequest")) { - PWINDOW->m_bNoMaximizeRequest = true; + } else if (r.szRule.starts_with("suppressevent")) { + CVarList vars(r.szRule, 0, 's', true); + for (size_t i = 1; i < vars.size(); ++i) { + if (vars[i] == "fullscreen") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN; + else if (vars[i] == "maximize") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_MAXIMIZE; + else if (vars[i] == "activate") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE; + else if (vars[i] == "activatefocus") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY; + else + Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]); + } } else if (r.szRule == "fullscreen") { requestsFullscreen = true; overridingNoFullscreen = true; @@ -270,6 +279,8 @@ void Events::listener_mapWindow(void* owner, void* data) { if (!pWorkspace) pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->m_iMonitorID, requestedWorkspaceName); + PWORKSPACE = pWorkspace; + PWINDOW->m_iWorkspaceID = pWorkspace->m_iID; PWINDOW->m_iMonitorID = pWorkspace->m_iMonitorID; @@ -443,9 +454,9 @@ void Events::listener_mapWindow(void* owner, void* data) { const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow; if (PWINDOW->m_sAdditionalConfigData.forceAllowsInput) { - PWINDOW->m_bNoFocus = false; - PWINDOW->m_bNoInitialFocus = false; - PWINDOW->m_bX11ShouldntFocus = false; + PWINDOW->m_sAdditionalConfigData.noFocus = false; + PWINDOW->m_bNoInitialFocus = false; + PWINDOW->m_bX11ShouldntFocus = false; } // check LS focus grab @@ -454,9 +465,9 @@ void Events::listener_mapWindow(void* owner, void* data) { if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.keyboard_interactive) PWINDOW->m_bNoInitialFocus = true; if (PWORKSPACE->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) { - if (*PNEWTAKESOVERFS == 0) + if (**PNEWTAKESOVERFS == 0) PWINDOW->m_bNoInitialFocus = true; - else if (*PNEWTAKESOVERFS == 2) + else if (**PNEWTAKESOVERFS == 2) g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), false, FULLSCREEN_INVALID); else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) requestsMaximize = true; @@ -464,14 +475,14 @@ void Events::listener_mapWindow(void* owner, void* data) { requestsFullscreen = true; } - if (!PWINDOW->m_bNoFocus && !PWINDOW->m_bNoInitialFocus && + if (!PWINDOW->m_sAdditionalConfigData.noFocus && !PWINDOW->m_bNoInitialFocus && (PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW)) { g_pCompositor->focusWindow(PWINDOW); - PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA); - PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sAdditionalConfigData.forceNoDim ? 0.f : *PDIMSTRENGTH); + PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(**PACTIVEALPHA); + PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sAdditionalConfigData.forceNoDim ? 0.f : **PDIMSTRENGTH); } else { - PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PINACTIVEALPHA); + PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(**PINACTIVEALPHA); PWINDOW->m_fDimPercent.setValueAndWarp(0); } @@ -504,8 +515,8 @@ void Events::listener_mapWindow(void* owner, void* data) { "XWayland Window Late"); } - if ((requestsFullscreen && (!PWINDOW->m_bNoFullscreenRequest || overridingNoFullscreen)) || (requestsMaximize && (!PWINDOW->m_bNoMaximizeRequest || overridingNoMaximize)) || - requestsFakeFullscreen) { + if ((requestsFullscreen && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) || overridingNoFullscreen)) || + (requestsMaximize && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) || overridingNoMaximize)) || requestsFakeFullscreen) { // fix fullscreen on requested (basically do a switcheroo) if (PWORKSPACE->m_bHasFullscreenWindow) { const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); @@ -540,7 +551,7 @@ void Events::listener_mapWindow(void* owner, void* data) { } // verify swallowing - if (*PSWALLOW && *PSWALLOWREGEX != STRVAL_EMPTY) { + if (**PSWALLOW && std::string{*PSWALLOWREGEX} != STRVAL_EMPTY) { // don't swallow ourselves std::regex rgx(*PSWALLOWREGEX); if (!std::regex_match(g_pXWaylandManager->getAppIDClass(PWINDOW), rgx)) { @@ -587,7 +598,7 @@ void Events::listener_mapWindow(void* owner, void* data) { if (finalFound) { bool valid = std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx); - if (*PSWALLOWEXREGEX != STRVAL_EMPTY) { + if (std::string{*PSWALLOWEXREGEX} != STRVAL_EMPTY) { std::regex exc(*PSWALLOWEXREGEX); valid = valid && !std::regex_match(g_pXWaylandManager->getTitle(finalFound), exc); @@ -639,7 +650,8 @@ void Events::listener_mapWindow(void* owner, void* data) { g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale); g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform); - g_pInputManager->sendMotionEventsToFocused(); + if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->constraintActive) + g_pInputManager->sendMotionEventsToFocused(); // fix some xwayland apps that don't behave nicely PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; @@ -793,6 +805,11 @@ void Events::listener_ackConfigure(void* owner, void* data) { void Events::listener_commitWindow(void* owner, void* data) { CWindow* PWINDOW = (CWindow*)owner; + if (!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->initial_commit) { + wlr_xdg_toplevel_set_size(PWINDOW->m_uSurface.xdg->toplevel, 0, 0); + return; + } + if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden()) return; @@ -803,8 +820,6 @@ void Events::listener_commitWindow(void* owner, void* data) { PWINDOW->m_pPendingSizeAck.reset(); } - PWINDOW->updateSurfaceOutputs(); - g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y, PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0); @@ -906,7 +921,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) { return; } - if (PWINDOW->isHidden() || PWINDOW->m_bNoFullscreenRequest) + if (PWINDOW->isHidden() || (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) return; bool requestedFullState = false; @@ -960,7 +975,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) { void Events::listener_activateXDG(wl_listener* listener, void* data) { const auto E = (wlr_xdg_activation_v1_request_activate_event*)data; - static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue; + static auto* const PFOCUSONACTIVATE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:focus_on_activate"); Debug::log(LOG, "Activate request for surface at {:x}", (uintptr_t)E->surface); @@ -969,7 +984,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) { const auto PWINDOW = g_pCompositor->getWindowFromSurface(E->surface); - if (!PWINDOW || PWINDOW == g_pCompositor->m_pLastWindow) + if (!PWINDOW || PWINDOW == g_pCompositor->m_pLastWindow || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE)) return; g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)}); @@ -977,7 +992,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) { PWINDOW->m_bIsUrgent = true; - if (!*PFOCUSONACTIVATE) + if (!**PFOCUSONACTIVATE || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY)) return; if (PWINDOW->m_bIsFloating) @@ -990,7 +1005,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) { void Events::listener_activateX11(void* owner, void* data) { const auto PWINDOW = (CWindow*)owner; - static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue; + static auto* const PFOCUSONACTIVATE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:focus_on_activate"); Debug::log(LOG, "X11 Activate request for window {}", PWINDOW); @@ -1001,17 +1016,20 @@ void Events::listener_activateX11(void* owner, void* data) { if (g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID()) return; + if (!wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland)) + return; + g_pCompositor->focusWindow(PWINDOW); return; } - if (PWINDOW == g_pCompositor->m_pLastWindow) + if (PWINDOW == g_pCompositor->m_pLastWindow || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE)) return; g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)PWINDOW)}); EMIT_HOOK_EVENT("urgent", PWINDOW); - if (!*PFOCUSONACTIVATE) + if (!**PFOCUSONACTIVATE || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY)) return; if (PWINDOW->m_bIsFloating) @@ -1052,13 +1070,10 @@ void Events::listener_configureX11(void* owner, void* data) { PWINDOW->m_vRealPosition.setValueAndWarp(LOGICALPOS); PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(E->width, E->height)); - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; - if (*PXWLFORCESCALEZERO) { - if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { - const Vector2D DELTA = PWINDOW->m_vRealSize.goalv() - PWINDOW->m_vRealSize.goalv() / PMONITOR->scale; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); + if (**PXWLFORCESCALEZERO) { + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale); - PWINDOW->m_vRealPosition.setValueAndWarp(PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0); - } } PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec(); @@ -1106,7 +1121,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { return; } - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOW->m_uSurface.xwayland->x, PWINDOW->m_uSurface.xwayland->y}); @@ -1121,7 +1136,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { if (abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->width, PWINDOW->m_uSurface.xwayland->height)); - if (*PXWLFORCESCALEZERO) { + if (**PXWLFORCESCALEZERO) { if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { const Vector2D DELTA = PWINDOW->m_vRealSize.goalv() - PWINDOW->m_vRealSize.goalv() / PMONITOR->scale; PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() / PMONITOR->scale); @@ -1139,8 +1154,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { g_pHyprRenderer->damageWindow(PWINDOW); PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goalv(); - PWINDOW->m_vReportedSize = PWINDOW->m_vRealSize.goalv(); - PWINDOW->m_vPendingReportedSize = PWINDOW->m_vReportedSize; + PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goalv(); } } @@ -1211,7 +1225,7 @@ void Events::listener_NewXDGDeco(wl_listener* listener, void* data) { void Events::listener_requestMaximize(void* owner, void* data) { const auto PWINDOW = (CWindow*)owner; - if (PWINDOW->m_bNoMaximizeRequest) + if (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) return; Debug::log(LOG, "Maximize request for {}", PWINDOW); diff --git a/src/helpers/AnimatedVariable.cpp b/src/helpers/AnimatedVariable.cpp index 50de0149..740e604f 100644 --- a/src/helpers/AnimatedVariable.cpp +++ b/src/helpers/AnimatedVariable.cpp @@ -75,6 +75,9 @@ float CAnimatedVariable::getPercent() { } float CAnimatedVariable::getCurveValue() { + if (!m_bIsBeingAnimated) + return 1.f; + const auto SPENT = getPercent(); if (SPENT >= 1.f) diff --git a/src/helpers/Box.cpp b/src/helpers/Box.cpp index 3d57bc79..59578f23 100644 --- a/src/helpers/Box.cpp +++ b/src/helpers/Box.cpp @@ -1,4 +1,8 @@ #include "Box.hpp" + +#include +#include + wlr_box CBox::wlr() { CBox rounded = roundInternal(); m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h}; @@ -105,6 +109,13 @@ CBox& CBox::expand(const double& value) { return *this; } +CBox& CBox::noNegativeSize() { + std::clamp(w, 0.0, std::numeric_limits::infinity()); + std::clamp(h, 0.0, std::numeric_limits::infinity()); + + return *this; +} + CBox CBox::roundInternal() { float newW = x + w - std::floor(x); float newH = y + h - std::floor(y); diff --git a/src/helpers/Box.hpp b/src/helpers/Box.hpp index 5cb7f17f..fd31a7b7 100644 --- a/src/helpers/Box.hpp +++ b/src/helpers/Box.hpp @@ -51,6 +51,7 @@ class CBox { CBox& transform(const wl_output_transform t, double w, double h); CBox& addExtents(const SWindowDecorationExtents& e); CBox& expand(const double& value); + CBox& noNegativeSize(); CBox copy() const; @@ -73,6 +74,8 @@ class CBox { double height; }; + double rot = 0; /* rad, ccw */ + // bool operator==(const CBox& rhs) const { return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 5eb99bfd..2905ce2b 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -159,6 +159,13 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString); } +void removeWLSignal(wl_listener* pListener) { + wl_list_remove(&pListener->link); + wl_list_init(&pListener->link); + + Debug::log(LOG, "Removed listener {:x}", (uintptr_t)pListener); +} + void handleNoop(struct wl_listener* listener, void* data) { // Do nothing } diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 1ccbdc0e..23fc6e5a 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -15,6 +15,7 @@ struct SCallstackFrameInfo { std::string absolutePath(const std::string&, const std::string&); void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString); +void removeWLSignal(wl_listener*); std::string escapeJSONStrings(const std::string& str); std::string removeBeginEndSpacesTabs(std::string); bool isNumber(const std::string&, bool allowfloat = false); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 3b4f4016..2dd2a2d9 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1,5 +1,7 @@ #include "Monitor.hpp" +#include "MiscFunctions.hpp" + #include "../Compositor.hpp" int ratHandler(void* data) { @@ -8,7 +10,7 @@ int ratHandler(void* data) { return 1; } -CMonitor::CMonitor() { +CMonitor::CMonitor() : state(this) { wlr_damage_ring_init(&damage); } @@ -43,8 +45,8 @@ void CMonitor::onConnect(bool noRule) { tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm if (m_bEnabled) { - wlr_output_enable(output, 1); - wlr_output_commit(output); + wlr_output_state_set_enabled(state.wlr(), true); + state.commit(); return; } @@ -54,17 +56,21 @@ void CMonitor::onConnect(bool noRule) { // remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description szDescription.erase(std::remove(szDescription.begin(), szDescription.end(), ','), szDescription.end()); + // field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix + szShortDescription = + removeBeginEndSpacesTabs(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : "")); + if (!wlr_backend_is_drm(output->backend)) createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable // get monitor rule that matches - SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(output->name, output->description ? output->description : ""); + SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this); // if it's disabled, disable and ignore if (monitorRule.disabled) { - wlr_output_set_scale(output, 1); - wlr_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL); + wlr_output_state_set_scale(state.wlr(), 1); + wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL); auto PREFSTATE = wlr_output_preferred_mode(output); @@ -72,9 +78,9 @@ void CMonitor::onConnect(bool noRule) { wlr_output_mode* mode; wl_list_for_each(mode, &output->modes, link) { - wlr_output_set_mode(output, PREFSTATE); + wlr_output_state_set_mode(state.wlr(), mode); - if (!wlr_output_test(output)) + if (!wlr_output_test_state(output, state.wlr())) continue; PREFSTATE = mode; @@ -83,13 +89,13 @@ void CMonitor::onConnect(bool noRule) { } if (PREFSTATE) - wlr_output_set_mode(output, PREFSTATE); + wlr_output_state_set_mode(state.wlr(), PREFSTATE); else Debug::log(WARN, "No mode found for disabled output {}", output->name); - wlr_output_enable(output, 0); + wlr_output_state_set_enabled(state.wlr(), 0); - if (!wlr_output_commit(output)) + if (!state.commit()) Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name); m_bEnabled = false; @@ -130,13 +136,28 @@ void CMonitor::onConnect(bool noRule) { m_bEnabled = true; - wlr_output_enable(output, 1); + wlr_output_state_set_enabled(state.wlr(), 1); // set mode, also applies if (!noRule) g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); - wlr_output_commit(output); + for (const auto& PTOUCHDEV : g_pInputManager->m_lTouchDevices) { + if (matchesStaticSelector(PTOUCHDEV.boundOutput)) { + Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV.name, szName); + wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTOUCHDEV.pWlrDevice, output); + } + } + + for (const auto& PTABLET : g_pInputManager->m_lTablets) { + if (matchesStaticSelector(PTABLET.boundOutput)) { + Debug::log(LOG, "Binding tablet {} to output {}", PTABLET.name, szName); + wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTABLET.wlrDevice, output); + } + } + + if (!state.commit()) + Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit"); wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y); @@ -162,6 +183,7 @@ void CMonitor::onConnect(bool noRule) { // g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)}); EMIT_HOOK_EVENT("monitorAdded", this); if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet @@ -283,9 +305,10 @@ void CMonitor::onDisconnect(bool destroy) { if (!destroy) wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output); - wlr_output_enable(output, false); + wlr_output_state_set_enabled(state.wlr(), false); - wlr_output_commit(output); + if (!state.commit()) + Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect"); if (g_pCompositor->m_pLastMonitor == this) g_pCompositor->setActiveMonitor(BACKUPMON); @@ -307,13 +330,11 @@ void CMonitor::onDisconnect(bool destroy) { } void CMonitor::addDamage(const pixman_region32_t* rg) { - static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue; - if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { + static auto* const PZOOMFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor"); + if (**PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { wlr_damage_ring_add_whole(&damage); g_pCompositor->scheduleFrameForMonitor(this); - } - - if (wlr_damage_ring_add(&damage, rg)) + } else if (wlr_damage_ring_add(&damage, rg)) g_pCompositor->scheduleFrameForMonitor(this); } @@ -322,8 +343,8 @@ void CMonitor::addDamage(const CRegion* rg) { } void CMonitor::addDamage(const CBox* box) { - static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue; - if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { + static auto* const PZOOMFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor"); + if (**PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { wlr_damage_ring_add_whole(&damage); g_pCompositor->scheduleFrameForMonitor(this); } @@ -336,6 +357,19 @@ bool CMonitor::isMirror() { return pMirrorOf != nullptr; } +bool CMonitor::matchesStaticSelector(const std::string& selector) const { + if (selector.starts_with("desc:")) { + // match by description + const auto DESCRIPTIONSELECTOR = selector.substr(5); + const auto DESCRIPTION = removeBeginEndSpacesTabs(szDescription.substr(0, szDescription.find_first_of('('))); + + return DESCRIPTIONSELECTOR == szDescription || DESCRIPTIONSELECTOR == DESCRIPTION; + } else { + // match by selector + return szName == selector; + } +} + int CMonitor::findAvailableDefaultWS() { for (size_t i = 1; i < INT32_MAX; ++i) { if (g_pCompositor->getWorkspaceByID(i)) @@ -415,7 +449,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) { pMirrorOf = nullptr; // set rule - const auto RULE = g_pConfigManager->getMonitorRuleFor(this->szName, this->output->description ? this->output->description : ""); + const auto RULE = g_pConfigManager->getMonitorRuleFor(*this); vecPosition = RULE.offset; @@ -502,7 +536,7 @@ float CMonitor::getDefaultScale() { return 1; } -void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove) { +void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool noMouseMove, bool noFocus) { if (!pWorkspace) return; @@ -533,13 +567,13 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool } } - if (!g_pCompositor->m_pLastMonitor->specialWorkspaceID) { - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; + if (!noFocus && !g_pCompositor->m_pLastMonitor->specialWorkspaceID) { + static auto* const PFOLLOWMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:follow_mouse"); CWindow* pWindow = pWorkspace->getLastFocusedWindow(); if (!pWindow) { - if (*PFOLLOWMOUSE == 1) - pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal()); + if (**PFOLLOWMOUSE == 1) + pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!pWindow) pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID); @@ -569,8 +603,8 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool g_pCompositor->updateSuspendedStates(); } -void CMonitor::changeWorkspace(const int& id, bool internal) { - changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal); +void CMonitor::changeWorkspace(const int& id, bool internal, bool noMouseMove, bool noFocus) { + changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal, noMouseMove, noFocus); } void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) { @@ -621,7 +655,7 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) { for (auto& w : g_pCompositor->m_vWindows) { if (w->m_iWorkspaceID == pWorkspace->m_iID) { w->m_iMonitorID = ID; - w->updateSurfaceOutputs(); + w->updateSurfaceScaleTransformDetails(); const auto MIDDLE = w->middle(); if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && w->m_iX11Type != 2) { @@ -678,3 +712,32 @@ void CMonitor::updateMatrix() { wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0); } } + +CMonitorState::CMonitorState(CMonitor* owner) { + m_pOwner = owner; + wlr_output_state_init(&m_state); +} + +CMonitorState::~CMonitorState() { + wlr_output_state_finish(&m_state); +} + +wlr_output_state* CMonitorState::wlr() { + return &m_state; +} + +void CMonitorState::clear() { + wlr_output_state_finish(&m_state); + m_state = {0}; + wlr_output_state_init(&m_state); +} + +bool CMonitorState::commit() { + bool ret = wlr_output_commit_state(m_pOwner->output, &m_state); + clear(); + return ret; +} + +bool CMonitorState::test() { + return wlr_output_test_state(m_pOwner->output, &m_state); +} diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 2b196c96..051c5305 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -25,6 +25,25 @@ struct SMonitorRule { std::optional vrr; }; +class CMonitor; + +// Class for wrapping the wlr state +class CMonitorState { + public: + CMonitorState(CMonitor* owner); + ~CMonitorState(); + + wlr_output_state* wlr(); + void clear(); + // commit() will also clear() + bool commit(); + bool test(); + + private: + wlr_output_state m_state = {0}; + CMonitor* m_pOwner; +}; + class CMonitor { public: CMonitor(); @@ -43,49 +62,51 @@ class CMonitor { float setScale = 1; // scale set by cfg float scale = 1; // real scale - std::string szName = ""; - std::string szDescription = ""; + std::string szName = ""; + std::string szDescription = ""; + std::string szShortDescription = ""; Vector2D vecReservedTopLeft = Vector2D(0, 0); Vector2D vecReservedBottomRight = Vector2D(0, 0); drmModeModeInfo customDrmMode = {}; + CMonitorState state; + // WLR stuff - wlr_damage_ring damage; - wlr_output* output = nullptr; - float refreshRate = 60; - int framesToSkip = 0; - int forceFullFrames = 0; - bool noFrameSchedule = false; - bool scheduledRecalc = false; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - bool gammaChanged = false; - float xwaylandScale = 1.f; - std::array projMatrix = {0}; + wlr_damage_ring damage; + wlr_output* output = nullptr; + float refreshRate = 60; + int framesToSkip = 0; + int forceFullFrames = 0; + bool noFrameSchedule = false; + bool scheduledRecalc = false; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + bool gammaChanged = false; + float xwaylandScale = 1.f; + std::array projMatrix = {0}; + std::optional forceSize; - bool dpmsStatus = true; - bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. - bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. - bool createdByUser = false; - uint32_t drmFormat = DRM_FORMAT_INVALID; - bool isUnsafeFallback = false; + bool dpmsStatus = true; + bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. + bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. + bool createdByUser = false; + uint32_t drmFormat = DRM_FORMAT_INVALID; + bool isUnsafeFallback = false; - bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after - bool renderingActive = false; + bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after + bool renderingActive = false; - wl_event_source* renderTimer = nullptr; // for RAT - bool RATScheduled = false; - CTimer lastPresentationTimer; + wl_event_source* renderTimer = nullptr; // for RAT + bool RATScheduled = false; + CTimer lastPresentationTimer; - SMonitorRule activeMonitorRule; + SMonitorRule activeMonitorRule; // mirroring CMonitor* pMirrorOf = nullptr; std::vector mirrors; - CRegion lastFrameDamage; // stores last frame damage - // for tearing CWindow* solitaryClient = nullptr; @@ -119,9 +140,10 @@ class CMonitor { void addDamage(const CBox* box); void setMirror(const std::string&); bool isMirror(); + bool matchesStaticSelector(const std::string& selector) const; float getDefaultScale(); - void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false); - void changeWorkspace(const int& id, bool internal = false); + void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); + void changeWorkspace(const int& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); void setSpecialWorkspace(CWorkspace* const pWorkspace); void setSpecialWorkspace(const int& id); void moveTo(const Vector2D& pos); diff --git a/src/helpers/Region.cpp b/src/helpers/Region.cpp index 8f180416..502bc198 100644 --- a/src/helpers/Region.cpp +++ b/src/helpers/Region.cpp @@ -112,6 +112,11 @@ CRegion& CRegion::scale(float scale) { return *this; } +CRegion& CRegion::scale(const Vector2D& scale) { + wlr_region_scale_xy(&m_rRegion, &m_rRegion, scale.x, scale.y); + return *this; +} + std::vector CRegion::getRects() const { std::vector result; diff --git a/src/helpers/Region.hpp b/src/helpers/Region.hpp index 972a2ba7..7ae334f7 100644 --- a/src/helpers/Region.hpp +++ b/src/helpers/Region.hpp @@ -49,6 +49,7 @@ class CRegion { CRegion& invert(pixman_box32_t* box); CRegion& invert(const CBox& box); CRegion& scale(float scale); + CRegion& scale(const Vector2D& scale); CBox getExtents(); bool containsPoint(const Vector2D& vec) const; bool empty() const; @@ -57,7 +58,8 @@ class CRegion { std::vector getRects() const; - pixman_region32_t* pixman() { + // + pixman_region32_t* pixman() { return &m_rRegion; } diff --git a/src/helpers/Splashes.hpp b/src/helpers/Splashes.hpp index 368615d6..e7a1cb5f 100644 --- a/src/helpers/Splashes.hpp +++ b/src/helpers/Splashes.hpp @@ -19,6 +19,21 @@ inline const std::vector SPLASHES = { "Compile, wait for 20 minutes, notice a new commit, compile again.", "To rice, or not to rice, that is the question.", "Now available on Fedora!", + "\"Hyprland is so good it starts with a capital letter\" - Hazel", + "\"please make this message a splash\" - eriedaberrie", + "\"the only wayland compositor powered by fried chicken\" - raf", + "\"This will never get into Hyprland\" - Flafy", + "\"Hyprland only gives you up on -git\" - fazzi", + "Segmentation fault (core dumped)", + "\"disabling hyprland logo is a war crime\" - vaxry", + "some basic startup code", + "\"I think I am addicted to hyprland\" - mathisbuilder", + "\"hyprland is the most important package in the arch repos\" - jacekpoz", + "Thanks Brodie!", + "Thanks fufexan!", + "Thanks raf!", + "You can't use --splash to change this message :)", + "Hyprland will overtake Gnome in popularity by [insert year]", // music reference / quote section "J'remue le ciel, le jour, la nuit.", "aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!", @@ -26,23 +41,6 @@ inline const std::vector SPLASHES = { "I see a red door and I want it painted black.", "Take on me, take me on...", "You spin me right round baby right round", - "Through the fire and the flames, we carry on!", - "Life could be a dream, sweetheart", - "We're off to never-never land", - "Just remember ALL CAPS when you spell the man name", - "The cake is a lie The cake is a lie The cake is a lie The cake is a lie", - "WE'RE SHAMELESS", - "Now I only want you gone", - "You Forget A Thousand Things Every Day Pal. Make Sure This Is One Of 'Em." - "The right man in the wrong place can make all the difference in the world." - "Truth Is, The Game Was Rigged From The Start." - "It's said war, war never changes. Men do, through the roads they walk." - "Victory shall be ours, it shall be swift, and it will be honest; purchased with blood." - "Patrolling the Mojave almost makes you wish for a nuclear winter." - "When life gives you lemons, don’t make lemonade…" - "You Picked The Wrong House, Fool!" - "I suck at life, but I bowl like an angel." - "Swaying to the symphony... of destruction!", "Stayin' alive, stayin' alive", "Say no way, say no way ya, no way!", "Ground control to Major Tom...", @@ -79,4 +77,4 @@ inline const std::vector SPLASHES = { "The AUR packages always work, except for the times they don't.", "Funny animation compositor woo" // clang-format on -}; +}; \ No newline at end of file diff --git a/src/helpers/SubsurfaceTree.cpp b/src/helpers/SubsurfaceTree.cpp index 5891411b..b4ea6d85 100644 --- a/src/helpers/SubsurfaceTree.cpp +++ b/src/helpers/SubsurfaceTree.cpp @@ -178,6 +178,9 @@ void Events::listener_mapSubsurface(void* owner, void* data) { Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface); subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner); + + if (subsurface->pWindowOwner) + subsurface->pWindowOwner->updateSurfaceScaleTransformDetails(); } void Events::listener_unmapSubsurface(void* owner, void* data) { @@ -222,8 +225,8 @@ void Events::listener_commitSubsurface(void* owner, void* data) { if (!g_pHyprRenderer->shouldRenderWindow(pNode->pWindowOwner)) { pNode->lastSize = pNode->pSurface->exists() ? Vector2D{pNode->pSurface->wlr()->current.width, pNode->pSurface->wlr()->current.height} : Vector2D{}; - static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue; - if (*PLOGDAMAGE) + static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage"); + if (**PLOGDAMAGE) Debug::log(LOG, "Refusing to commit damage from {} because it's invisible.", pNode->pWindowOwner); return; } @@ -259,13 +262,11 @@ void Events::listener_commitSubsurface(void* owner, void* data) { // tearing: if solitary, redraw it. This still might be a single surface window const auto PMONITOR = g_pCompositor->getMonitorFromID(pNode->pWindowOwner->m_iMonitorID); - if (PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear && + if (PMONITOR && PMONITOR->solitaryClient == pNode->pWindowOwner && pNode->pWindowOwner->canBeTorn() && PMONITOR->tearingState.canTear && pNode->pSurface->wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) { - CRegion damageBox; - wlr_surface_get_effective_damage(pNode->pSurface->wlr(), damageBox.pixman()); + CRegion damageBox{&pNode->pSurface->wlr()->buffer_damage}; if (!damageBox.empty()) { - if (PMONITOR->tearingState.busy) { PMONITOR->tearingState.frameScheduledWhileBusy = true; } else { diff --git a/src/helpers/Vector2D.cpp b/src/helpers/Vector2D.cpp index 7bc6412d..c74da0b1 100644 --- a/src/helpers/Vector2D.cpp +++ b/src/helpers/Vector2D.cpp @@ -12,6 +12,11 @@ Vector2D::Vector2D() { y = 0; } +Vector2D::Vector2D(const Hyprlang::VEC2& ref) { + x = ref.x; + y = ref.y; +} + Vector2D::~Vector2D() {} double Vector2D::normalize() { @@ -42,14 +47,10 @@ double Vector2D::distance(const Vector2D& other) const { return std::sqrt(dx * dx + dy * dy); } -bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const { - const auto a = ((p2.y - p3.y) * (x - p3.x) + (p3.x - p2.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); - const auto b = ((p3.y - p1.y) * (x - p3.x) + (p1.x - p3.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); - const auto c = 1 - a - b; - - return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; -} - double Vector2D::size() const { return std::sqrt(x * x + y * y); } + +Vector2D Vector2D::getComponentMax(const Vector2D& other) const { + return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y)); +} diff --git a/src/helpers/Vector2D.hpp b/src/helpers/Vector2D.hpp index 34b06862..309ed3bc 100644 --- a/src/helpers/Vector2D.hpp +++ b/src/helpers/Vector2D.hpp @@ -3,12 +3,14 @@ #include #include #include "../macros.hpp" +#include class Vector2D { public: Vector2D(double, double); Vector2D(); ~Vector2D(); + Vector2D(const Hyprlang::VEC2&); double x = 0; double y = 0; @@ -88,12 +90,12 @@ class Vector2D { double distance(const Vector2D& other) const; double size() const; - Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D()) const; + Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D{-1, -1}) const; Vector2D floor() const; Vector2D round() const; - bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const; + Vector2D getComponentMax(const Vector2D& other) const; }; /** diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index be0fd005..6cc9b5d5 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -127,15 +127,17 @@ struct SKeyboard { bool active = false; bool enabled = true; - xkb_layout_index_t activeLayout = 0; + xkb_layout_index_t activeLayout = 0; + xkb_state* xkbTranslationState = nullptr; std::string name = ""; std::string xkbFilePath = ""; SStringRuleNames currentRules; - int repeatRate = 0; - int repeatDelay = 0; - int numlockOn = -1; + int repeatRate = 0; + int repeatDelay = 0; + int numlockOn = -1; + bool resolveBindsBySym = false; // For the list lookup bool operator==(const SKeyboard& rhs) const { @@ -259,6 +261,8 @@ struct STablet { std::string name = ""; + std::string boundOutput = ""; + // bool operator==(const STablet& b) const { return wlrDevice == b.wlrDevice; @@ -410,3 +414,13 @@ struct STearingController { return pWlrHint == other.pWlrHint; } }; + +struct SShortcutInhibitor { + wlr_keyboard_shortcuts_inhibitor_v1* pWlrInhibitor = nullptr; + + DYNLISTENER(destroy); + + bool operator==(const SShortcutInhibitor& other) { + return pWlrInhibitor == other.pWlrInhibitor; + } +}; diff --git a/src/helpers/WLListener.cpp b/src/helpers/WLListener.cpp index 402c999c..f499b929 100644 --- a/src/helpers/WLListener.cpp +++ b/src/helpers/WLListener.cpp @@ -7,13 +7,15 @@ void handleWrapped(wl_listener* listener, void* data) { CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); - g_pWatchdog->startWatching(); + if (g_pWatchdog) + g_pWatchdog->startWatching(); try { pWrap->m_pSelf->emit(data); } catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); } - g_pWatchdog->endWatching(); + if (g_pWatchdog) + g_pWatchdog->endWatching(); } CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function callback, void* pOwner) { diff --git a/src/helpers/WLSurface.cpp b/src/helpers/WLSurface.cpp index 3528a72d..25f11feb 100644 --- a/src/helpers/WLSurface.cpp +++ b/src/helpers/WLSurface.cpp @@ -52,6 +52,29 @@ Vector2D CWLSurface::getViewporterCorrectedSize() const { Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height}; } +CRegion CWLSurface::logicalDamage() const { + CRegion damage{&m_pWLRSurface->buffer_damage}; + damage.transform(m_pWLRSurface->current.transform, m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height); + damage.scale(1.0 / m_pWLRSurface->current.scale); + + const auto VPSIZE = getViewporterCorrectedSize(); + const auto CORRECTVEC = correctSmallVec(); + + if (m_pWLRSurface->current.viewport.has_src) { + damage.intersect(CBox{std::floor(m_pWLRSurface->current.viewport.src.x), std::floor(m_pWLRSurface->current.viewport.src.y), + std::ceil(m_pWLRSurface->current.viewport.src.width), std::ceil(m_pWLRSurface->current.viewport.src.height)}); + } + + const auto SCALEDSRCSIZE = m_pWLRSurface->current.viewport.has_src ? + Vector2D{m_pWLRSurface->current.viewport.src.width, m_pWLRSurface->current.viewport.src.height} * m_pWLRSurface->current.scale : + Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height}; + + damage.scale({VPSIZE.x / SCALEDSRCSIZE.x, VPSIZE.y / SCALEDSRCSIZE.y}); + damage.translate(CORRECTVEC); + + return damage; +} + void CWLSurface::destroy() { if (!m_pWLRSurface) return; @@ -60,11 +83,11 @@ void CWLSurface::destroy() { m_pWLRSurface->data = nullptr; m_pOwner = nullptr; - if (g_pCompositor->m_pLastFocus == m_pWLRSurface) + if (g_pCompositor && g_pCompositor->m_pLastFocus == m_pWLRSurface) g_pCompositor->m_pLastFocus = nullptr; - if (g_pInputManager->m_pLastMouseSurface == m_pWLRSurface) + if (g_pInputManager && g_pInputManager->m_pLastMouseSurface == m_pWLRSurface) g_pInputManager->m_pLastMouseSurface = nullptr; - if (g_pHyprRenderer->m_sLastCursorData.surf == m_pWLRSurface) + if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf == m_pWLRSurface) g_pHyprRenderer->m_sLastCursorData.surf.reset(); m_pWLRSurface = nullptr; diff --git a/src/helpers/WLSurface.hpp b/src/helpers/WLSurface.hpp index faef4353..eab4ac48 100644 --- a/src/helpers/WLSurface.hpp +++ b/src/helpers/WLSurface.hpp @@ -1,6 +1,7 @@ #pragma once #include "../defines.hpp" +#include "Region.hpp" class CWindow; @@ -23,13 +24,20 @@ class CWLSurface { bool small() const; // means surface is smaller than the requested size Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces Vector2D getViewporterCorrectedSize() const; + CRegion logicalDamage() const; // allow stretching. Useful for plugins. bool m_bFillIgnoreSmall = false; // if present, means this is a base surface of a window. Cleaned on unassign() - CWindow* m_pOwner = nullptr; + CWindow* m_pOwner = nullptr; + // track surface data and avoid dupes + float m_fLastScale = 0; + int m_iLastScale = 0; + wl_output_transform m_eLastTransform = (wl_output_transform)-1; + + // CWLSurface& operator=(wlr_surface* pSurface) { destroy(); m_pWLRSurface = pSurface; diff --git a/src/helpers/Watchdog.cpp b/src/helpers/Watchdog.cpp index 2a26ee29..ce4d2707 100644 --- a/src/helpers/Watchdog.cpp +++ b/src/helpers/Watchdog.cpp @@ -13,7 +13,7 @@ CWatchdog::CWatchdog() { m_iMainThreadPID = pthread_self(); m_pWatchdog = std::make_unique([this] { - static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; + static auto* const PTIMEOUT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout"); while (1337) { std::unique_lock lk(m_mWatchdogMutex); @@ -21,7 +21,7 @@ CWatchdog::CWatchdog() { if (!m_bWillWatch) m_cvWatchdogCondition.wait(lk, [this] { return m_bNotified; }); else { - if (m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(*PTIMEOUT * 1000.0)), [this] { return m_bNotified; }) == false) + if (m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(**PTIMEOUT * 1000.0)), [this] { return m_bNotified; }) == false) pthread_kill(m_iMainThreadPID, SIGUSR1); } @@ -37,9 +37,9 @@ CWatchdog::CWatchdog() { } void CWatchdog::startWatching() { - static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; + static auto* const PTIMEOUT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout"); - if (*PTIMEOUT == 0) + if (**PTIMEOUT == 0) return; m_tTriggered = std::chrono::high_resolution_clock::now(); diff --git a/src/helpers/Workspace.cpp b/src/helpers/Workspace.cpp index 213607d0..ad8ae4ad 100644 --- a/src/helpers/Workspace.cpp +++ b/src/helpers/Workspace.cpp @@ -24,6 +24,10 @@ CWorkspace::CWorkspace(int monitorID, std::string name, bool special) { m_vRenderOffset.registerVar(); m_fAlpha.registerVar(); + const auto RULEFORTHIS = g_pConfigManager->getWorkspaceRuleFor(this); + if (RULEFORTHIS.defaultName.has_value()) + m_szName = RULEFORTHIS.defaultName.value(); + g_pEventManager->postEvent({"createworkspace", m_szName}); EMIT_HOOK_EVENT("createWorkspace", this); } @@ -38,8 +42,8 @@ CWorkspace::~CWorkspace() { } void CWorkspace::startAnim(bool in, bool left, bool instant) { - const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle; - const auto PWORKSPACEGAP = &g_pConfigManager->getConfigValuePtr("general:gaps_workspaces")->intValue; + const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle; + static auto* const PWORKSPACEGAP = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:gaps_workspaces"); if (ANIMSTYLE.starts_with("slidefade")) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); @@ -91,7 +95,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) { } else if (ANIMSTYLE == "slidevert") { // fallback is slide const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - const auto YDISTANCE = PMONITOR->vecSize.y + *PWORKSPACEGAP; + const auto YDISTANCE = PMONITOR->vecSize.y + **PWORKSPACEGAP; m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. @@ -104,7 +108,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) { } else { // fallback is slide const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - const auto XDISTANCE = PMONITOR->vecSize.x + *PWORKSPACEGAP; + const auto XDISTANCE = PMONITOR->vecSize.x + **PWORKSPACEGAP; m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 53ddb86e..0f4a5f87 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -4,13 +4,12 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) { if (children[0]) { - static auto* const PSMARTSPLIT = &g_pConfigManager->getConfigValuePtr("dwindle:smart_split")->intValue; - static auto* const PPRESERVESPLIT = &g_pConfigManager->getConfigValuePtr("dwindle:preserve_split")->intValue; - static auto* const PFLMULT = &g_pConfigManager->getConfigValuePtr("dwindle:split_width_multiplier")->floatValue; + static auto* const PSMARTSPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:smart_split"); + static auto* const PPRESERVESPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:preserve_split"); + static auto* const PFLMULT = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("dwindle:split_width_multiplier"); - if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0) { - splitTop = box.h * *PFLMULT > box.w; - } + if (**PPRESERVESPLIT == 0 && **PSMARTSPLIT == 0) + splitTop = box.h * **PFLMULT > box.w; if (verticalOverride == true) splitTop = true; @@ -22,13 +21,13 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid if (SPLITSIDE) { // split left/right const float FIRSTSIZE = box.w / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}; - children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}; + children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize(); + children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize(); } else { // split top/bottom const float FIRSTSIZE = box.h / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}; - children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}; + children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize(); + children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize(); } children[0]->recalcSizePosRecursive(force); @@ -64,6 +63,21 @@ SDwindleNodeData* CHyprDwindleLayout::getFirstNodeOnWorkspace(const int& id) { return nullptr; } +SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const int& id, const Vector2D& point) { + SDwindleNodeData* res = nullptr; + double distClosest = -1; + for (auto& n : m_lDwindleNodesData) { + if (n.workspaceID == id && n.pWindow && g_pCompositor->windowValidMapped(n.pWindow)) { + auto distAnother = vecToRectDistanceSquared(point, n.box.pos(), n.box.pos() + n.box.size()); + if (!res || distAnother < distClosest) { + res = &n; + distClosest = distAnother; + } + } + } + return res; +} + SDwindleNodeData* CHyprDwindleLayout::getNodeFromWindow(CWindow* pWindow) { for (auto& n : m_lDwindleNodesData) { if (n.pWindow == pWindow && !n.isNode) @@ -120,9 +134,11 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for PWINDOW->updateSpecialRenderData(); - static auto* const PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue; - static auto* const PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue; - static auto* const PNOGAPSWHENONLY = &g_pConfigManager->getConfigValuePtr("dwindle:no_gaps_when_only")->intValue; + static auto* const PNOGAPSWHENONLY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:no_gaps_when_only"); + static auto* const PGAPSINDATA = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:gaps_in"); + static auto* const PGAPSOUTDATA = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:gaps_out"); + auto* const PGAPSIN = (CCssGapData*)(*PGAPSINDATA)->getData(); + auto* const PGAPSOUT = (CCssGapData*)(*PGAPSOUTDATA)->getData(); auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); @@ -141,10 +157,10 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for const auto NODESONWORKSPACE = getNodesOnWorkspace(PWINDOW->m_iWorkspaceID); - if (*PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) && + if (**PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) && (NODESONWORKSPACE == 1 || (PWINDOW->m_bIsFullscreen && g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) { - PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(*PNOGAPSWHENONLY == 2); + PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(**PNOGAPSWHENONLY == 2); PWINDOW->m_sSpecialRenderData.decorate = WORKSPACERULE.decorate.value_or(true); PWINDOW->m_sSpecialRenderData.rounding = false; PWINDOW->m_sSpecialRenderData.shadow = false; @@ -164,9 +180,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for auto calcPos = PWINDOW->m_vPosition; auto calcSize = PWINDOW->m_vSize; - const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut : gapsIn, DISPLAYTOP ? gapsOut : gapsIn); + const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut.left : gapsIn.left, DISPLAYTOP ? gapsOut.top : gapsIn.top); - const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut : gapsIn, DISPLAYBOTTOM ? gapsOut : gapsIn); + const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut.right : gapsIn.right, DISPLAYBOTTOM ? gapsOut.bottom : gapsIn.bottom); calcPos = calcPos + OFFSETTOPLEFT; calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT; @@ -201,9 +217,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) { // if special, we adjust the coords a bit - static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("dwindle:special_scale_factor")->floatValue; + static auto* const PSCALEFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("dwindle:special_scale_factor"); - CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR}; + CBox wb = {calcPos + (calcSize - calcSize * **PSCALEFACTOR) / 2.f, calcSize * **PSCALEFACTOR}; wb.round(); // avoid rounding mess PWINDOW->m_vRealPosition = wb.pos(); @@ -211,10 +227,13 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for g_pXWaylandManager->setWindowSize(PWINDOW, wb.size()); } else { - PWINDOW->m_vRealSize = calcSize; - PWINDOW->m_vRealPosition = calcPos; + CBox wb = {calcPos, calcSize}; + wb.round(); // avoid rounding mess - g_pXWaylandManager->setWindowSize(PWINDOW, calcSize); + PWINDOW->m_vRealSize = wb.size(); + PWINDOW->m_vRealPosition = wb.pos(); + + g_pXWaylandManager->setWindowSize(PWINDOW, wb.size()); } if (force) { @@ -238,8 +257,8 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); - static auto* const PUSEACTIVE = &g_pConfigManager->getConfigValuePtr("dwindle:use_active_for_splits")->intValue; - static auto* const PDEFAULTSPLIT = &g_pConfigManager->getConfigValuePtr("dwindle:default_split_ratio")->floatValue; + static auto* const PUSEACTIVE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:use_active_for_splits"); + static auto* const PDEFAULTSPLIT = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("dwindle:default_split_ratio"); if (direction != DIRECTION_DEFAULT && overrideDirection == DIRECTION_DEFAULT) overrideDirection = direction; @@ -254,26 +273,25 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire const auto MOUSECOORDS = m_vOverrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); - const auto TARGETCOORDS = PMONITOR->ID == MONFROMCURSOR->ID && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR) ? pWindow->middle() : MOUSECOORDS; if (PMONITOR->ID == MONFROMCURSOR->ID && - (PNODE->workspaceID == PMONITOR->activeWorkspace || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->specialWorkspaceID)) && !*PUSEACTIVE) { - OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS)); + (PNODE->workspaceID == PMONITOR->activeWorkspace || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->specialWorkspaceID)) && !**PUSEACTIVE) { + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); - // happens on reserved area - if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0) - OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace); + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); - } else if (*PUSEACTIVE) { + } else if (**PUSEACTIVE) { if (g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow != pWindow && g_pCompositor->m_pLastWindow->m_iWorkspaceID == pWindow->m_iWorkspaceID && g_pCompositor->m_pLastWindow->m_bIsMapped) { OPENINGON = getNodeFromWindow(g_pCompositor->m_pLastWindow); } else { - OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowTiled(TARGETCOORDS)); + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); } - if (!OPENINGON && g_pCompositor->getWindowsOnWorkspace(PNODE->workspaceID) > 0) - OPENINGON = getFirstNodeOnWorkspace(PMONITOR->activeWorkspace); + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); + } else OPENINGON = getFirstNodeOnWorkspace(pWindow->m_iWorkspaceID); @@ -325,8 +343,8 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire && pWindow->canBeGroupedInto(OPENINGON->pWindow) && !m_vOverrideFocalPoint) { // we are not moving window m_lDwindleNodesData.remove(*PNODE); - static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue; - (*USECURRPOS ? OPENINGON->pWindow : OPENINGON->pWindow->getGroupTail())->insertWindowToGroup(pWindow); + static const auto* USECURRPOS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:insert_after_current"); + (**USECURRPOS ? OPENINGON->pWindow : OPENINGON->pWindow->getGroupTail())->insertWindowToGroup(pWindow); OPENINGON->pWindow->setGroupCurrent(pWindow); pWindow->applyGroupRules(); @@ -349,17 +367,17 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire NEWPARENT->workspaceID = OPENINGON->workspaceID; NEWPARENT->pParent = OPENINGON->pParent; NEWPARENT->isNode = true; // it is a node - NEWPARENT->splitRatio = std::clamp(*PDEFAULTSPLIT, 0.1f, 1.9f); + NEWPARENT->splitRatio = std::clamp(**PDEFAULTSPLIT, 0.1f, 1.9f); - const auto PWIDTHMULTIPLIER = &g_pConfigManager->getConfigValuePtr("dwindle:split_width_multiplier")->floatValue; + const auto PWIDTHMULTIPLIER = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("dwindle:split_width_multiplier"); // if cursor over first child, make it first, etc - const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER; + const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * **PWIDTHMULTIPLIER; NEWPARENT->splitTop = !SIDEBYSIDE; - static auto* const PFORCESPLIT = &g_pConfigManager->getConfigValuePtr("dwindle:force_split")->intValue; - static auto* const PERMANENTDIRECTIONOVERRIDE = &g_pConfigManager->getConfigValuePtr("dwindle:permanent_direction_override")->intValue; - static auto* const PSMARTSPLIT = &g_pConfigManager->getConfigValuePtr("dwindle:smart_split")->intValue; + static auto* const PFORCESPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:force_split"); + static auto* const PERMANENTDIRECTIONOVERRIDE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:permanent_direction_override"); + static auto* const PSMARTSPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:smart_split"); bool horizontalOverride = false; bool verticalOverride = false; @@ -383,37 +401,44 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire } // whether or not the override persists after opening one window - if (*PERMANENTDIRECTIONOVERRIDE == 0) + if (**PERMANENTDIRECTIONOVERRIDE == 0) overrideDirection = DIRECTION_DEFAULT; - } else if (*PSMARTSPLIT == 1) { - const auto tl = NEWPARENT->box.pos(); - const auto tr = NEWPARENT->box.pos() + Vector2D(NEWPARENT->box.w, 0); - const auto bl = NEWPARENT->box.pos() + Vector2D(0, NEWPARENT->box.h); - const auto br = NEWPARENT->box.pos() + NEWPARENT->box.size(); - const auto cc = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; + } else if (**PSMARTSPLIT == 1) { + const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; + const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w; + const auto DELTA = MOUSECOORDS - PARENT_CENTER; + const auto DELTA_SLOPE = DELTA.y / DELTA.x; - if (TARGETCOORDS.inTriangle(tl, tr, cc)) { - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; - } else if (TARGETCOORDS.inTriangle(tr, cc, br)) { - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } else if (TARGETCOORDS.inTriangle(br, bl, cc)) { - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; + if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) { + if (DELTA.x > 0) { + // right + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // left + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } } else { - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; + if (DELTA.y > 0) { + // bottom + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // top + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } } - } else if (*PFORCESPLIT == 0 || !pWindow->m_bFirstMap) { + } else if (**PFORCESPLIT == 0 || !pWindow->m_bFirstMap) { if ((SIDEBYSIDE && - VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / **PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || (!SIDEBYSIDE && - VECINRECT(TARGETCOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / **PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { // we are hovering over the first node, make PNODE first. NEWPARENT->children[1] = OPENINGON; NEWPARENT->children[0] = PNODE; @@ -423,7 +448,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire NEWPARENT->children[1] = PNODE; } } else { - if (*PFORCESPLIT == 1) { + if (**PFORCESPLIT == 1) { NEWPARENT->children[1] = OPENINGON; NEWPARENT->children[0] = PNODE; } else { @@ -442,7 +467,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire } // Update the children - if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) { + if (!verticalOverride && (NEWPARENT->box.w * **PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) { // split left/right -> forced OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; PNODE->box = {Vector2D(NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; @@ -586,8 +611,8 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn return; } - const auto PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue; - const auto PSMARTRESIZING = &g_pConfigManager->getConfigValuePtr("dwindle:smart_resizing")->intValue; + const auto PANIMATE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes"); + const auto PSMARTRESIZING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("dwindle:smart_resizing"); // get some data about our window const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); @@ -624,11 +649,13 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn else PWINDOW->m_vPseudoSize.y -= pixResize.y * 2; - PWINDOW->m_vPseudoSize.x = std::clamp(PWINDOW->m_vPseudoSize.x, 30.0, PNODE->box.w); - PWINDOW->m_vPseudoSize.y = std::clamp(PWINDOW->m_vPseudoSize.y, 30.0, PNODE->box.h); + CBox wbox = PNODE->box; + wbox.round(); + + PWINDOW->m_vPseudoSize = {std::clamp(PWINDOW->m_vPseudoSize.x, 30.0, wbox.w), std::clamp(PWINDOW->m_vPseudoSize.y, 30.0, wbox.h)}; PWINDOW->m_vLastFloatingSize = PWINDOW->m_vPseudoSize; - PNODE->recalcSizePosRecursive(*PANIMATE == 0); + PNODE->recalcSizePosRecursive(**PANIMATE == 0); return; } @@ -642,7 +669,7 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (DISPLAYBOTTOM && DISPLAYTOP) allowedMovement.y = 0; - if (*PSMARTRESIZING == 1) { + if (**PSMARTRESIZING == 1) { // Identify inner and outer nodes for both directions SDwindleNodeData* PVOUTER = nullptr; SDwindleNodeData* PVINNER = nullptr; @@ -676,14 +703,14 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (PHINNER) { const auto ORIGINAL = PHINNER->box.w; - PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PHOUTER->pParent->recalcSizePosRecursive(**PANIMATE == 0); if (PHINNER->pParent->children[0] == PHINNER) PHINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); else PHINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); - PHINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PHINNER->pParent->recalcSizePosRecursive(**PANIMATE == 0); } else - PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PHOUTER->pParent->recalcSizePosRecursive(**PANIMATE == 0); } if (PVOUTER) { @@ -691,14 +718,14 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (PVINNER) { const auto ORIGINAL = PVINNER->box.h; - PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PVOUTER->pParent->recalcSizePosRecursive(**PANIMATE == 0); if (PVINNER->pParent->children[0] == PVINNER) PVINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); else PVINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); - PVINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PVINNER->pParent->recalcSizePosRecursive(**PANIMATE == 0); } else - PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + PVOUTER->pParent->recalcSizePosRecursive(**PANIMATE == 0); } } else { // get the correct containers to apply splitratio to @@ -717,11 +744,11 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (PARENTSIDEBYSIDE) { allowedMovement.x *= 2.f / PPARENT->box.w; PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + PPARENT->recalcSizePosRecursive(**PANIMATE == 0); } else { allowedMovement.y *= 2.f / PPARENT->box.h; PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + PPARENT->recalcSizePosRecursive(**PANIMATE == 0); } return; @@ -736,11 +763,11 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn if (PARENTSIDEBYSIDE) { allowedMovement.x *= 2.f / PPARENT->box.w; PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + PPARENT->recalcSizePosRecursive(**PANIMATE == 0); } else { allowedMovement.y *= 2.f / PPARENT->box.h; PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + PPARENT->recalcSizePosRecursive(**PANIMATE == 0); } return; @@ -755,8 +782,8 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn SIDECONTAINER->splitRatio = std::clamp(SIDECONTAINER->splitRatio + allowedMovement.x, 0.1, 1.9); TOPCONTAINER->splitRatio = std::clamp(TOPCONTAINER->splitRatio + allowedMovement.y, 0.1, 1.9); - SIDECONTAINER->recalcSizePosRecursive(*PANIMATE == 0); - TOPCONTAINER->recalcSizePosRecursive(*PANIMATE == 0); + SIDECONTAINER->recalcSizePosRecursive(**PANIMATE == 0); + TOPCONTAINER->recalcSizePosRecursive(**PANIMATE == 0); } } @@ -776,10 +803,21 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree return; } + // save position and size if floating + if (pWindow->m_bIsFloating && on) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = pWindow->m_vRealSize.goalv(); + } + // otherwise, accept it. pWindow->m_bIsFullscreen = on; PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow; + pWindow->updateDynamicRules(); + pWindow->updateWindowDecos(); + g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)}); EMIT_HOOK_EVENT("fullscreen", pWindow); @@ -800,14 +838,6 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree PWORKSPACE->m_efFullscreenMode = fullscreenMode; - // save position and size if floating - if (pWindow->m_bIsFloating) { - pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - } - // apply new pos and size being monitors' box if (fullscreenMode == FULLSCREEN_FULL) { pWindow->m_vRealPosition = PMONITOR->vecPosition; @@ -824,7 +854,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscree pWindow->m_vPosition = fakeNode.box.pos(); pWindow->m_vSize = fakeNode.box.size(); - applyNodeDataToWindow(&fakeNode); + applyNodeDataToWindow(&fakeNode, true); } } @@ -967,6 +997,8 @@ std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::str const auto ARGS = CVarList(message, 0, ' '); if (ARGS[0] == "togglesplit") { toggleSplit(header.pWindow); + } else if (ARGS[0] == "swapsplit") { + swapSplit(header.pWindow); } else if (ARGS[0] == "preselect") { std::string direction = ARGS[1]; @@ -1020,6 +1052,20 @@ void CHyprDwindleLayout::toggleSplit(CWindow* pWindow) { PNODE->pParent->recalcSizePosRecursive(); } +void CHyprDwindleLayout::swapSplit(CWindow* pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE || !PNODE->pParent) + return; + + if (pWindow->m_bIsFullscreen) + return; + + std::swap(PNODE->pParent->children[0], PNODE->pParent->children[1]); + + PNODE->pParent->recalcSizePosRecursive(); +} + void CHyprDwindleLayout::replaceWindowDataWith(CWindow* from, CWindow* to) { const auto PNODE = getNodeFromWindow(from); diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index c2d47d88..f5501b28 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -79,9 +79,11 @@ class CHyprDwindleLayout : public IHyprLayout { void applyNodeDataToWindow(SDwindleNodeData*, bool force = false); SDwindleNodeData* getNodeFromWindow(CWindow*); SDwindleNodeData* getFirstNodeOnWorkspace(const int&); + SDwindleNodeData* getClosestNodeOnWorkspace(const int&, const Vector2D&); SDwindleNodeData* getMasterNodeOnWorkspace(const int&); void toggleSplit(CWindow*); + void swapSplit(CWindow*); eDirection overrideDirection = DIRECTION_DEFAULT; diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index 6a03ac80..49e20435 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -87,7 +87,7 @@ void IHyprLayout::onWindowCreatedFloating(CWindow* pWindow) { desiredGeometry.y = xy.y; } - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); if (!PMONITOR) { Debug::log(ERR, "{:m} has an invalid monitor in onWindowCreatedFloating!!!", pWindow); @@ -151,7 +151,7 @@ void IHyprLayout::onWindowCreatedFloating(CWindow* pWindow) { } } - if (*PXWLFORCESCALEZERO && pWindow->m_bIsX11) + if (**PXWLFORCESCALEZERO && pWindow->m_bIsX11) pWindow->m_vRealSize = pWindow->m_vRealSize.goalv() / PMONITOR->scale; if (pWindow->m_bX11DoesntWantBorders || (pWindow->m_bIsX11 && pWindow->m_uSurface.xwayland->override_redirect)) { @@ -268,15 +268,15 @@ void IHyprLayout::onEndDragWindow() { } else if (g_pInputManager->dragMode == MBIND_MOVE) { g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - CWindow* pWindow = g_pCompositor->vectorToWindowIdeal(MOUSECOORDS, DRAGGINGWINDOW); + CWindow* pWindow = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING | FLOATING_ONLY, DRAGGINGWINDOW); - if (pWindow && pWindow->m_bIsFloating) { + if (pWindow) { if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGINGWINDOW)) return; if (pWindow->m_sGroupData.pNextWindow && DRAGGINGWINDOW->canBeGroupedInto(pWindow)) { - static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue; - (*USECURRPOS ? pWindow : pWindow->getGroupTail())->insertWindowToGroup(DRAGGINGWINDOW); + static const auto* USECURRPOS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:insert_after_current"); + (**USECURRPOS ? pWindow : pWindow->getGroupTail())->insertWindowToGroup(DRAGGINGWINDOW); pWindow->setGroupCurrent(DRAGGINGWINDOW); DRAGGINGWINDOW->updateWindowDecos(); @@ -309,13 +309,12 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) { const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y); const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y); - static auto* const PANIMATEMOUSE = &g_pConfigManager->getConfigValuePtr("misc:animate_mouse_windowdragging")->intValue; - static auto* const PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue; + static auto* const PANIMATEMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:animate_mouse_windowdragging"); + static auto* const PANIMATE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes"); if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count() < - 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate && - (*PANIMATEMOUSE || *PANIMATE))) + 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate)) return; TIMER = std::chrono::high_resolution_clock::now(); @@ -329,7 +328,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) { CBox wb = {m_vBeginDragPositionXY + DELTA, DRAGGINGWINDOW->m_vRealSize.goalv()}; wb.round(); - if (*PANIMATEMOUSE) + if (**PANIMATEMOUSE) DRAGGINGWINDOW->m_vRealPosition = wb.pos(); else DRAGGINGWINDOW->m_vRealPosition.setValueAndWarp(wb.pos()); @@ -387,7 +386,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) { CBox wb = {newPos, newSize}; wb.round(); - if (*PANIMATE) { + if (**PANIMATE) { DRAGGINGWINDOW->m_vRealSize = wb.size(); DRAGGINGWINDOW->m_vRealPosition = wb.pos(); } else { @@ -469,16 +468,18 @@ void IHyprLayout::changeWindowFloatingMode(CWindow* pWindow) { g_pCompositor->changeWindowZOrder(pWindow, true); + CBox wb = {pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize}; + wb.round(); + if (DELTALESSTHAN(pWindow->m_vRealSize.vec().x, pWindow->m_vLastFloatingSize.x, 10) && DELTALESSTHAN(pWindow->m_vRealSize.vec().y, pWindow->m_vLastFloatingSize.y, 10)) { - pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f + Vector2D{10, 10}; - pWindow->m_vRealSize = pWindow->m_vLastFloatingSize - Vector2D{20, 20}; + wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}}; } - pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f; - pWindow->m_vRealSize = pWindow->m_vLastFloatingSize; + pWindow->m_vRealPosition = wb.pos(); + pWindow->m_vRealSize = wb.size(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = wb.pos(); + pWindow->m_vPosition = wb.size(); g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID)); @@ -529,7 +530,7 @@ CWindow* IHyprLayout::getNextWindowCandidate(CWindow* pWindow) { // find whether there is a floating window below this one for (auto& w : g_pCompositor->m_vWindows) { if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && !w->m_bX11ShouldntFocus && - !w->m_bNoFocus && w.get() != pWindow) { + !w->m_sAdditionalConfigData.noFocus && w.get() != pWindow) { if (VECINRECT((pWindow->m_vSize / 2.f + pWindow->m_vPosition), w->m_vPosition.x, w->m_vPosition.y, w->m_vPosition.x + w->m_vSize.x, w->m_vPosition.y + w->m_vSize.y)) { return w.get(); @@ -542,13 +543,14 @@ CWindow* IHyprLayout::getNextWindowCandidate(CWindow* pWindow) { return m_pLastTiledWindow; // if we don't, let's try to find any window that is in the middle - if (const auto PWINDOWCANDIDATE = g_pCompositor->vectorToWindowIdeal(pWindow->middle()); PWINDOWCANDIDATE && PWINDOWCANDIDATE != pWindow) + if (const auto PWINDOWCANDIDATE = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); + PWINDOWCANDIDATE && PWINDOWCANDIDATE != pWindow) return PWINDOWCANDIDATE; // if not, floating window for (auto& w : g_pCompositor->m_vWindows) { if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && w->m_iX11Type != 2 && w->m_iWorkspaceID == pWindow->m_iWorkspaceID && !w->m_bX11ShouldntFocus && - !w->m_bNoFocus && w.get() != pWindow) + !w->m_sAdditionalConfigData.noFocus && w.get() != pWindow) return w.get(); } @@ -557,7 +559,7 @@ CWindow* IHyprLayout::getNextWindowCandidate(CWindow* pWindow) { } // if it was a tiled window, we first try to find the window that will replace it. - auto pWindowCandidate = g_pCompositor->vectorToWindowIdeal(pWindow->middle()); + auto pWindowCandidate = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!pWindowCandidate) pWindowCandidate = g_pCompositor->getTopLeftWindowOnWorkspace(pWindow->m_iWorkspaceID); diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp index 65f85691..6a3cd492 100644 --- a/src/layout/MasterLayout.cpp +++ b/src/layout/MasterLayout.cpp @@ -1,6 +1,7 @@ #include "MasterLayout.hpp" #include "../Compositor.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "config/ConfigDataValues.hpp" #include SMasterNodeData* CHyprMasterLayout::getNodeFromWindow(CWindow* pWindow) { @@ -39,11 +40,11 @@ SMasterWorkspaceData* CHyprMasterLayout::getMasterWorkspaceData(const int& ws) { } //create on the fly if it doesn't exist yet - const auto PWORKSPACEDATA = &m_lMasterWorkspacesData.emplace_back(); - PWORKSPACEDATA->workspaceID = ws; - const auto orientation = &g_pConfigManager->getConfigValuePtr("master:orientation")->strValue; - const auto layoutoptsForWs = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(ws)).layoutopts; - auto orientationForWs = *orientation; + const auto PWORKSPACEDATA = &m_lMasterWorkspacesData.emplace_back(); + PWORKSPACEDATA->workspaceID = ws; + const auto orientation = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("master:orientation"); + const auto layoutoptsForWs = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(ws)).layoutopts; + std::string orientationForWs = *orientation; if (layoutoptsForWs.contains("orientation")) orientationForWs = layoutoptsForWs.at("orientation"); @@ -80,20 +81,20 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc if (pWindow->m_bIsFloating) return; - static auto* const PNEWTOP = &g_pConfigManager->getConfigValuePtr("master:new_on_top")->intValue; + static auto* const PNEWTOP = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:new_on_top"); const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); - const auto PNODE = *PNEWTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back(); + const auto PNODE = **PNEWTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back(); PNODE->workspaceID = pWindow->m_iWorkspaceID; PNODE->pWindow = pWindow; - static auto* const PNEWISMASTER = &g_pConfigManager->getConfigValuePtr("master:new_is_master")->intValue; + static auto* const PNEWISMASTER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:new_is_master"); const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID); - static auto* const PMFACT = &g_pConfigManager->getConfigValuePtr("master:mfact")->floatValue; - float lastSplitPercent = *PMFACT; + static auto* const PMFACT = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("master:mfact"); + float lastSplitPercent = **PMFACT; auto OPENINGON = isWindowTiled(g_pCompositor->m_pLastWindow) && g_pCompositor->m_pLastWindow->m_iWorkspaceID == pWindow->m_iWorkspaceID ? getNodeFromWindow(g_pCompositor->m_pLastWindow) : @@ -112,8 +113,8 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc m_lMasterNodesData.remove(*PNODE); - static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue; - (*USECURRPOS ? OPENINGON->pWindow : OPENINGON->pWindow->getGroupTail())->insertWindowToGroup(pWindow); + static const auto* USECURRPOS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:insert_after_current"); + (**USECURRPOS ? OPENINGON->pWindow : OPENINGON->pWindow->getGroupTail())->insertWindowToGroup(pWindow); OPENINGON->pWindow->setGroupCurrent(pWindow); pWindow->applyGroupRules(); @@ -128,14 +129,14 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc pWindow->applyGroupRules(); - static auto* const PDROPATCURSOR = &g_pConfigManager->getConfigValuePtr("master:drop_at_cursor")->intValue; + static auto* const PDROPATCURSOR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:drop_at_cursor"); const auto PWORKSPACEDATA = getMasterWorkspaceData(pWindow->m_iWorkspaceID); eOrientation orientation = PWORKSPACEDATA->orientation; const auto NODEIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *PNODE); bool forceDropAsMaster = false; // if dragging window to move, drop it at the cursor position instead of bottom/top of stack - if (*PDROPATCURSOR && g_pInputManager->dragMode == MBIND_MOVE) { + if (**PDROPATCURSOR && g_pInputManager->dragMode == MBIND_MOVE) { if (WINDOWSONWORKSPACE > 2) { for (auto it = m_lMasterNodesData.begin(); it != m_lMasterNodesData.end(); ++it) { if (it->workspaceID != pWindow->m_iWorkspaceID) @@ -191,7 +192,7 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc } } - if ((*PNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) || WINDOWSONWORKSPACE == 1 || (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) || + if ((**PNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) || WINDOWSONWORKSPACE == 1 || (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) || forceDropAsMaster) { for (auto& nd : m_lMasterNodesData) { if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) { @@ -239,14 +240,14 @@ void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) { const auto WORKSPACEID = PNODE->workspaceID; const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID); - static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue; + static const auto* SMALLSPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:allow_small_split"); pWindow->updateSpecialRenderData(); if (pWindow->m_bIsFullscreen) g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL); - if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) { + if (PNODE->isMaster && (MASTERSLEFT <= 1 || **SMALLSPLIT == 1)) { // find a new master from top of the list for (auto& nd : m_lMasterNodesData) { if (!nd.isMaster && nd.workspaceID == WORKSPACEID) { @@ -338,8 +339,8 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { eOrientation orientation = PWORKSPACEDATA->orientation; bool centerMasterWindow = false; - static auto* const ALWAYSCENTER = &g_pConfigManager->getConfigValuePtr("master:always_center_master")->intValue; - static auto* const PSMARTRESIZING = &g_pConfigManager->getConfigValuePtr("master:smart_resizing")->intValue; + static auto* const ALWAYSCENTER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:always_center_master"); + static auto* const PSMARTRESIZING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:smart_resizing"); const auto MASTERS = getMastersOnWorkspace(PWORKSPACE->m_iID); const auto WINDOWS = getNodesOnWorkspace(PWORKSPACE->m_iID); @@ -348,7 +349,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { const auto WSPOS = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft; if (orientation == ORIENTATION_CENTER) { - if (STACKWINDOWS >= 2 || (*ALWAYSCENTER == 1)) { + if (STACKWINDOWS >= 2 || (**ALWAYSCENTER == 1)) { centerMasterWindow = true; } else { orientation = ORIENTATION_LEFT; @@ -361,7 +362,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { float masterAccumulatedSize = 0; float slaveAccumulatedSize = 0; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { // check the total width and height so that later // if larger/smaller than screen size them down/up for (auto& nd : m_lMasterNodesData) { @@ -399,7 +400,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { if (WIDTH > widthLeft * 0.9f && mastersLeft > 1) WIDTH = widthLeft * 0.9f; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { nd.percSize *= WSSIZE.x / masterAccumulatedSize; WIDTH = masterAverageSize * nd.percSize; } @@ -436,7 +437,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { if (HEIGHT > heightLeft * 0.9f && mastersLeft > 1) HEIGHT = heightLeft * 0.9f; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { nd.percSize *= WSSIZE.y / masterAccumulatedSize; HEIGHT = masterAverageSize * nd.percSize; } @@ -473,7 +474,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { if (WIDTH > widthLeft * 0.9f && slavesLeft > 1) WIDTH = widthLeft * 0.9f; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { nd.percSize *= WSSIZE.x / slaveAccumulatedSize; WIDTH = slaveAverageSize * nd.percSize; } @@ -503,7 +504,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) HEIGHT = heightLeft * 0.9f; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { nd.percSize *= WSSIZE.y / slaveAccumulatedSize; HEIGHT = slaveAverageSize * nd.percSize; } @@ -534,7 +535,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { const float slaveAverageHeightR = WSSIZE.y / slavesLeftR; float slaveAccumulatedHeightL = 0; float slaveAccumulatedHeightR = 0; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { for (auto& nd : m_lMasterNodesData) { if (nd.workspaceID != PWORKSPACE->m_iID || nd.isMaster) continue; @@ -569,7 +570,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) { if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) HEIGHT = heightLeft * 0.9f; - if (*PSMARTRESIZING) { + if (**PSMARTRESIZING) { if (onRight) { nd.percSize *= WSSIZE.y / slaveAccumulatedHeightR; HEIGHT = slaveAverageHeightR * nd.percSize; @@ -633,10 +634,12 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { PWINDOW->updateSpecialRenderData(); - static auto* const PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue; - static auto* const PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue; - static auto* const PNOGAPSWHENONLY = &g_pConfigManager->getConfigValuePtr("master:no_gaps_when_only")->intValue; - static auto* const PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue; + static auto* const PNOGAPSWHENONLY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:no_gaps_when_only"); + static auto* const PANIMATE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes"); + static auto* const PGAPSINDATA = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:gaps_in"); + static auto* const PGAPSOUTDATA = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("general:gaps_out"); + auto* const PGAPSIN = (CCssGapData*)(*PGAPSINDATA)->getData(); + auto* const PGAPSOUT = (CCssGapData*)(*PGAPSOUTDATA)->getData(); auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); @@ -649,11 +652,11 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { PWINDOW->m_vSize = pNode->size; PWINDOW->m_vPosition = pNode->position; - if (*PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) && + if (**PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) && (getNodesOnWorkspace(PWINDOW->m_iWorkspaceID) == 1 || (PWINDOW->m_bIsFullscreen && g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) { - PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(*PNOGAPSWHENONLY == 2); + PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(**PNOGAPSWHENONLY == 2); PWINDOW->m_sSpecialRenderData.decorate = WORKSPACERULE.decorate.value_or(true); PWINDOW->m_sSpecialRenderData.rounding = false; PWINDOW->m_sSpecialRenderData.shadow = false; @@ -673,9 +676,9 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { auto calcPos = PWINDOW->m_vPosition; auto calcSize = PWINDOW->m_vSize; - const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut : gapsIn, DISPLAYTOP ? gapsOut : gapsIn); + const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut.left : gapsIn.left, DISPLAYTOP ? gapsOut.top : gapsIn.top); - const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut : gapsIn, DISPLAYBOTTOM ? gapsOut : gapsIn); + const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut.right : gapsIn.right, DISPLAYBOTTOM ? gapsOut.bottom : gapsIn.bottom); calcPos = calcPos + OFFSETTOPLEFT; calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT; @@ -685,9 +688,9 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight); if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) { - static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("master:special_scale_factor")->floatValue; + static auto* const PSCALEFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("master:special_scale_factor"); - CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR}; + CBox wb = {calcPos + (calcSize - calcSize * **PSCALEFACTOR) / 2.f, calcSize * **PSCALEFACTOR}; wb.round(); // avoid rounding mess PWINDOW->m_vRealPosition = wb.pos(); @@ -736,11 +739,11 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); const auto PWORKSPACEDATA = getMasterWorkspaceData(PMONITOR->activeWorkspace); - static auto* const ALWAYSCENTER = &g_pConfigManager->getConfigValuePtr("master:always_center_master")->intValue; - static auto* const PSMARTRESIZING = &g_pConfigManager->getConfigValuePtr("master:smart_resizing")->intValue; + static auto* const ALWAYSCENTER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:always_center_master"); + static auto* const PSMARTRESIZING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:smart_resizing"); eOrientation orientation = PWORKSPACEDATA->orientation; - bool centered = orientation == ORIENTATION_CENTER && (*ALWAYSCENTER == 1); + bool centered = orientation == ORIENTATION_CENTER && (**ALWAYSCENTER == 1); double delta = 0; const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_vPosition.y + PWINDOW->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); @@ -797,7 +800,7 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne const auto SIZE = isStackVertical ? WSSIZE.y / nodesInSameColumn : WSSIZE.x / nodesInSameColumn; if (RESIZEDELTA != 0 && nodesInSameColumn > 1) { - if (!*PSMARTRESIZING) { + if (!**PSMARTRESIZING) { PNODE->percSize = std::clamp(PNODE->percSize + RESIZEDELTA / SIZE, 0.05, 1.95); } else { const auto NODEIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *PNODE); @@ -879,10 +882,21 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen return; } + // save position and size if floating + if (pWindow->m_bIsFloating && on) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); + pWindow->m_vSize = pWindow->m_vRealSize.goalv(); + } + // otherwise, accept it. pWindow->m_bIsFullscreen = on; PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow; + pWindow->updateDynamicRules(); + pWindow->updateWindowDecos(); + g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)}); EMIT_HOOK_EVENT("fullscreen", pWindow); @@ -903,14 +917,6 @@ void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreen PWORKSPACE->m_efFullscreenMode = fullscreenMode; - // save position and size if floating - if (pWindow->m_bIsFloating) { - pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv(); - pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vPosition = pWindow->m_vRealPosition.goalv(); - pWindow->m_vSize = pWindow->m_vRealSize.goalv(); - } - // apply new pos and size being monitors' box if (fullscreenMode == FULLSCREEN_FULL) { pWindow->m_vRealPosition = PMONITOR->vecPosition; @@ -1052,8 +1058,8 @@ bool CHyprMasterLayout::prepareLoseFocus(CWindow* pWindow) { //if the current window is fullscreen, make it normal again if we are about to lose focus if (pWindow->m_bIsFullscreen) { g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL); - static auto* const INHERIT = &g_pConfigManager->getConfigValuePtr("master:inherit_fullscreen")->intValue; - return *INHERIT == 1; + static auto* const INHERIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:inherit_fullscreen"); + return **INHERIT == 1; } return false; @@ -1230,9 +1236,9 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri const auto WINDOWS = getNodesOnWorkspace(header.pWindow->m_iWorkspaceID); const auto MASTERS = getMastersOnWorkspace(header.pWindow->m_iWorkspaceID); - static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue; + static const auto* SMALLSPLIT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("master:allow_small_split"); - if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0) + if (MASTERS + 2 > WINDOWS && **SMALLSPLIT == 0) return 0; prepareLoseFocus(header.pWindow); @@ -1328,7 +1334,13 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri const auto PWINDOW = header.pWindow; const auto PNODE = getNodeFromWindow(PWINDOW); - const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!PNODE) + return 0; + + const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!OLDMASTER) + return 0; + const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER); for (auto& nd : m_lMasterNodesData) { @@ -1350,7 +1362,13 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri const auto PWINDOW = header.pWindow; const auto PNODE = getNodeFromWindow(PWINDOW); - const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!PNODE) + return 0; + + const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!OLDMASTER) + return 0; + const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER); for (auto& nd : m_lMasterNodesData | std::views::reverse) { diff --git a/src/main.cpp b/src/main.cpp index afcbb3c1..e1f43ad0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,10 +34,6 @@ int main(int argc, char** argv) { setenv("_JAVA_AWT_WM_NONREPARENTING", "1", 1); setenv("MOZ_ENABLE_WAYLAND", "1", 1); setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1); - setenv("XDG_SESSION_TYPE", "wayland", 1); - setenv("QT_QPA_PLATFORM", "wayland", 1); - setenv("SDL_VIDEODRIVER", "wayland", 1); - setenv("OZONE_PLATFORM", "wayland", 1); // parse some args std::string configPath; @@ -89,7 +85,7 @@ int main(int argc, char** argv) { } if (!ignoreSudo && Init::isSudo()) { - std::cerr << "[ ERROR ] Hyprland was launched with superuser priveleges, but the privileges check is not omitted.\n"; + std::cerr << "[ ERROR ] Hyprland was launched with superuser privileges, but the privileges check is not omitted.\n"; std::cerr << " Hint: Use the --i-am-really-stupid flag to omit that check.\n"; return 1; diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp index 120e6f49..23584827 100644 --- a/src/managers/AnimationManager.cpp +++ b/src/managers/AnimationManager.cpp @@ -53,12 +53,12 @@ void CAnimationManager::tick() { bool animGlobalDisabled = false; - static auto* const PANIMENABLED = &g_pConfigManager->getConfigValuePtr("animations:enabled")->intValue; + static auto* const PANIMENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("animations:enabled"); - if (!*PANIMENABLED) + if (!**PANIMENABLED) animGlobalDisabled = true; - static auto* const PSHADOWSENABLED = &g_pConfigManager->getConfigValuePtr("decoration:drop_shadow")->intValue; + static auto* const PSHADOWSENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:drop_shadow"); const auto DEFAULTBEZIER = m_mBezierCurves.find("default"); @@ -66,7 +66,7 @@ void CAnimationManager::tick() { for (auto& av : m_vActiveAnimatedVariables) { - if (av->m_eDamagePolicy == AVARDAMAGE_SHADOW && !*PSHADOWSENABLED) { + if (av->m_eDamagePolicy == AVARDAMAGE_SHADOW && !**PSHADOWSENABLED) { av->warp(false); animationEndedVars.push_back(av); continue; @@ -223,6 +223,11 @@ void CAnimationManager::tick() { } else if (PLAYER) { if (PLAYER->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || PLAYER->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); + + // some fucking layers miss 1 pixel??? + CBox expandBox = WLRBOXPREV; + expandBox.expand(5); + g_pHyprRenderer->damageBox(&expandBox); } break; } @@ -232,9 +237,9 @@ void CAnimationManager::tick() { // TODO: move this to the border class // damage only the border. - static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue; - const auto ROUNDINGSIZE = *PROUNDING + 1; - const auto BORDERSIZE = PWINDOW->getRealBorderSize(); + const auto ROUNDING = PWINDOW->rounding(); + const auto ROUNDINGSIZE = ROUNDING + 1; + const auto BORDERSIZE = PWINDOW->getRealBorderSize(); // damage for old box g_pHyprRenderer->damageBox(WLRBOXPREV.x - BORDERSIZE, WLRBOXPREV.y - BORDERSIZE, WLRBOXPREV.width + 2 * BORDERSIZE, BORDERSIZE + ROUNDINGSIZE); // top diff --git a/src/managers/EventManager.cpp b/src/managers/EventManager.cpp index 64b4f6bf..8365c8c8 100644 --- a/src/managers/EventManager.cpp +++ b/src/managers/EventManager.cpp @@ -19,13 +19,69 @@ CEventManager::CEventManager() {} int fdHandleWrite(int fd, uint32_t mask, void* data) { + const auto PEVMGR = (CEventManager*)data; + return PEVMGR->onFDWrite(fd, mask); +} - auto removeFD = [&](int fd) -> void { - const auto ACCEPTEDFDS = (std::deque>*)data; - for (auto it = ACCEPTEDFDS->begin(); it != ACCEPTEDFDS->end();) { +int socket2HandleWrite(int fd, uint32_t mask, void* data) { + const auto PEVMGR = (CEventManager*)data; + return PEVMGR->onSocket2Write(fd, mask); +} + +void CEventManager::startThread() { + + m_iSocketFD = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + + if (m_iSocketFD < 0) { + Debug::log(ERR, "Couldn't start the Hyprland Socket 2. (1) IPC will not work."); + return; + } + + sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX}; + std::string socketPath = "/tmp/hypr/" + g_pCompositor->m_szInstanceSignature + "/.socket2.sock"; + strncpy(SERVERADDRESS.sun_path, socketPath.c_str(), sizeof(SERVERADDRESS.sun_path) - 1); + + bind(m_iSocketFD, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)); + + // 10 max queued. + listen(m_iSocketFD, 10); + + m_pEventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, socket2HandleWrite, this); +} + +int CEventManager::onSocket2Write(int fd, uint32_t mask) { + + if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) { + Debug::log(ERR, "Socket2 hangup?? IPC broke"); + wl_event_source_remove(m_pEventSource); + close(fd); + return 0; + } + + sockaddr_in clientAddress; + socklen_t clientSize = sizeof(clientAddress); + const auto ACCEPTEDCONNECTION = accept4(m_iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC | SOCK_NONBLOCK); + + if (ACCEPTEDCONNECTION > 0) { + Debug::log(LOG, "Socket2 accepted a new client at FD {}", ACCEPTEDCONNECTION); + + // add to event loop so we can close it when we need to + m_dAcceptedSocketFDs.push_back( + std::make_pair<>(ACCEPTEDCONNECTION, wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, ACCEPTEDCONNECTION, WL_EVENT_READABLE, fdHandleWrite, this))); + } else { + Debug::log(ERR, "Socket2 failed receiving connection, errno: {}", errno); + close(fd); + } + + return 0; +} + +int CEventManager::onFDWrite(int fd, uint32_t mask) { + auto removeFD = [this](int fd) -> void { + for (auto it = m_dAcceptedSocketFDs.begin(); it != m_dAcceptedSocketFDs.end();) { if (it->first == fd) { wl_event_source_remove(it->second); // remove this fd listener - it = ACCEPTEDFDS->erase(it); + it = m_dAcceptedSocketFDs.erase(it); } else { it++; } @@ -58,52 +114,6 @@ int fdHandleWrite(int fd, uint32_t mask, void* data) { return 0; } -void CEventManager::startThread() { - m_tThread = std::thread([&]() { - const auto SOCKET = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - - if (SOCKET < 0) { - Debug::log(ERR, "Couldn't start the Hyprland Socket 2. (1) IPC will not work."); - return; - } - - sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX}; - std::string socketPath = "/tmp/hypr/" + g_pCompositor->m_szInstanceSignature + "/.socket2.sock"; - strncpy(SERVERADDRESS.sun_path, socketPath.c_str(), sizeof(SERVERADDRESS.sun_path) - 1); - - bind(SOCKET, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)); - - // 10 max queued. - listen(SOCKET, 10); - - sockaddr_in clientAddress; - socklen_t clientSize = sizeof(clientAddress); - - Debug::log(LOG, "Hypr socket 2 started at {}", socketPath); - - while (1) { - const auto ACCEPTEDCONNECTION = accept4(SOCKET, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC); - - if (ACCEPTEDCONNECTION > 0) { - // new connection! - - int flagsNew = fcntl(ACCEPTEDCONNECTION, F_GETFL, 0); - fcntl(ACCEPTEDCONNECTION, F_SETFL, flagsNew | O_NONBLOCK); - - Debug::log(LOG, "Socket 2 accepted a new client at FD {}", ACCEPTEDCONNECTION); - - // add to event loop so we can close it when we need to - m_dAcceptedSocketFDs.push_back( - {ACCEPTEDCONNECTION, wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, ACCEPTEDCONNECTION, WL_EVENT_READABLE, fdHandleWrite, &m_dAcceptedSocketFDs)}); - } - } - - close(SOCKET); - }); - - m_tThread.detach(); -} - void CEventManager::flushEvents() { eventQueueMutex.lock(); diff --git a/src/managers/EventManager.hpp b/src/managers/EventManager.hpp index 68cda1ee..ed681dba 100644 --- a/src/managers/EventManager.hpp +++ b/src/managers/EventManager.hpp @@ -21,6 +21,11 @@ class CEventManager { std::thread m_tThread; + int m_iSocketFD = -1; + + int onSocket2Write(int fd, uint32_t mask); + int onFDWrite(int fd, uint32_t mask); + private: void flushEvents(); @@ -28,6 +33,8 @@ class CEventManager { std::deque m_dQueuedEvents; std::deque> m_dAcceptedSocketFDs; + + wl_event_source* m_pEventSource = nullptr; }; inline std::unique_ptr g_pEventManager; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 899003d8..481b8987 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -7,6 +7,7 @@ #include #include +#include #if defined(__linux__) #include #elif defined(__NetBSD__) || defined(__OpenBSD__) @@ -18,64 +19,66 @@ CKeybindManager::CKeybindManager() { // initialize all dispatchers - m_mDispatchers["exec"] = spawn; - m_mDispatchers["execr"] = spawnRaw; - m_mDispatchers["killactive"] = killActive; - m_mDispatchers["closewindow"] = kill; - m_mDispatchers["togglefloating"] = toggleActiveFloating; - m_mDispatchers["workspace"] = changeworkspace; - m_mDispatchers["renameworkspace"] = renameWorkspace; - m_mDispatchers["fullscreen"] = fullscreenActive; - m_mDispatchers["fakefullscreen"] = fakeFullscreenActive; - m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; - m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; - m_mDispatchers["pseudo"] = toggleActivePseudo; - m_mDispatchers["movefocus"] = moveFocusTo; - m_mDispatchers["movewindow"] = moveActiveTo; - m_mDispatchers["swapwindow"] = swapActive; - m_mDispatchers["centerwindow"] = centerWindow; - m_mDispatchers["togglegroup"] = toggleGroup; - m_mDispatchers["changegroupactive"] = changeGroupActive; - m_mDispatchers["movegroupwindow"] = moveGroupWindow; - m_mDispatchers["togglesplit"] = toggleSplit; - m_mDispatchers["splitratio"] = alterSplitRatio; - m_mDispatchers["focusmonitor"] = focusMonitor; - m_mDispatchers["movecursortocorner"] = moveCursorToCorner; - m_mDispatchers["movecursor"] = moveCursor; - m_mDispatchers["workspaceopt"] = workspaceOpt; - m_mDispatchers["exit"] = exitHyprland; - m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; - m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; - m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; - m_mDispatchers["forcerendererreload"] = forceRendererReload; - m_mDispatchers["resizeactive"] = resizeActive; - m_mDispatchers["moveactive"] = moveActive; - m_mDispatchers["cyclenext"] = circleNext; - m_mDispatchers["focuswindowbyclass"] = focusWindow; - m_mDispatchers["focuswindow"] = focusWindow; - m_mDispatchers["submap"] = setSubmap; - m_mDispatchers["pass"] = pass; - m_mDispatchers["layoutmsg"] = layoutmsg; - m_mDispatchers["toggleopaque"] = toggleOpaque; - m_mDispatchers["dpms"] = dpms; - m_mDispatchers["movewindowpixel"] = moveWindow; - m_mDispatchers["resizewindowpixel"] = resizeWindow; - m_mDispatchers["swapnext"] = swapnext; - m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; - m_mDispatchers["pin"] = pinActive; - m_mDispatchers["mouse"] = mouse; - m_mDispatchers["bringactivetotop"] = bringActiveToTop; - m_mDispatchers["alterzorder"] = alterZOrder; - m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; - m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; - m_mDispatchers["lockgroups"] = lockGroups; - m_mDispatchers["lockactivegroup"] = lockActiveGroup; - m_mDispatchers["moveintogroup"] = moveIntoGroup; - m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; - m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; - m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; - m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; - m_mDispatchers["global"] = global; + m_mDispatchers["exec"] = spawn; + m_mDispatchers["execr"] = spawnRaw; + m_mDispatchers["killactive"] = killActive; + m_mDispatchers["closewindow"] = kill; + m_mDispatchers["togglefloating"] = toggleActiveFloating; + m_mDispatchers["workspace"] = changeworkspace; + m_mDispatchers["renameworkspace"] = renameWorkspace; + m_mDispatchers["fullscreen"] = fullscreenActive; + m_mDispatchers["fakefullscreen"] = fakeFullscreenActive; + m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; + m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; + m_mDispatchers["pseudo"] = toggleActivePseudo; + m_mDispatchers["movefocus"] = moveFocusTo; + m_mDispatchers["movewindow"] = moveActiveTo; + m_mDispatchers["swapwindow"] = swapActive; + m_mDispatchers["centerwindow"] = centerWindow; + m_mDispatchers["togglegroup"] = toggleGroup; + m_mDispatchers["changegroupactive"] = changeGroupActive; + m_mDispatchers["movegroupwindow"] = moveGroupWindow; + m_mDispatchers["togglesplit"] = toggleSplit; + m_mDispatchers["swapsplit"] = swapSplit; + m_mDispatchers["splitratio"] = alterSplitRatio; + m_mDispatchers["focusmonitor"] = focusMonitor; + m_mDispatchers["movecursortocorner"] = moveCursorToCorner; + m_mDispatchers["movecursor"] = moveCursor; + m_mDispatchers["workspaceopt"] = workspaceOpt; + m_mDispatchers["exit"] = exitHyprland; + m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; + m_mDispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor; + m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; + m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; + m_mDispatchers["forcerendererreload"] = forceRendererReload; + m_mDispatchers["resizeactive"] = resizeActive; + m_mDispatchers["moveactive"] = moveActive; + m_mDispatchers["cyclenext"] = circleNext; + m_mDispatchers["focuswindowbyclass"] = focusWindow; + m_mDispatchers["focuswindow"] = focusWindow; + m_mDispatchers["submap"] = setSubmap; + m_mDispatchers["pass"] = pass; + m_mDispatchers["layoutmsg"] = layoutmsg; + m_mDispatchers["toggleopaque"] = toggleOpaque; + m_mDispatchers["dpms"] = dpms; + m_mDispatchers["movewindowpixel"] = moveWindow; + m_mDispatchers["resizewindowpixel"] = resizeWindow; + m_mDispatchers["swapnext"] = swapnext; + m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; + m_mDispatchers["pin"] = pinActive; + m_mDispatchers["mouse"] = mouse; + m_mDispatchers["bringactivetotop"] = bringActiveToTop; + m_mDispatchers["alterzorder"] = alterZOrder; + m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; + m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; + m_mDispatchers["lockgroups"] = lockGroups; + m_mDispatchers["lockactivegroup"] = lockActiveGroup; + m_mDispatchers["moveintogroup"] = moveIntoGroup; + m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; + m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; + m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; + m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; + m_mDispatchers["global"] = global; m_tScrollTimer.reset(); @@ -95,7 +98,7 @@ void CKeybindManager::addKeybind(SKeybind kb) { void CKeybindManager::removeKeybind(uint32_t mod, const std::string& key) { for (auto it = m_lKeybinds.begin(); it != m_lKeybinds.end(); ++it) { if (isNumber(key) && std::stoi(key) > 9) { - const auto KEYNUM = std::stoi(key); + const uint32_t KEYNUM = std::stoi(key); if (it->modmask == mod && it->keycode == KEYNUM) { it = m_lKeybinds.erase(it); @@ -137,6 +140,22 @@ uint32_t CKeybindManager::stringToModMask(std::string mods) { return modMask; } +uint32_t CKeybindManager::keycodeToModifier(xkb_keycode_t keycode) { + switch (keycode - 8) { + case KEY_LEFTMETA: return WLR_MODIFIER_LOGO; + case KEY_RIGHTMETA: return WLR_MODIFIER_LOGO; + case KEY_LEFTSHIFT: return WLR_MODIFIER_SHIFT; + case KEY_RIGHTSHIFT: return WLR_MODIFIER_SHIFT; + case KEY_LEFTCTRL: return WLR_MODIFIER_CTRL; + case KEY_RIGHTCTRL: return WLR_MODIFIER_CTRL; + case KEY_LEFTALT: return WLR_MODIFIER_ALT; + case KEY_RIGHTALT: return WLR_MODIFIER_ALT; + case KEY_CAPSLOCK: return WLR_MODIFIER_CAPS; + case KEY_NUMLOCK: return WLR_MODIFIER_MOD2; + default: return 0; + } +} + void CKeybindManager::updateXKBTranslationState() { if (m_pXKBTranslationState) { xkb_keymap_unref(xkb_state_get_keymap(m_pXKBTranslationState)); @@ -145,19 +164,26 @@ void CKeybindManager::updateXKBTranslationState() { m_pXKBTranslationState = nullptr; } - const auto FILEPATH = g_pConfigManager->getString("input:kb_file"); - const auto RULES = g_pConfigManager->getString("input:kb_rules"); - const auto MODEL = g_pConfigManager->getString("input:kb_model"); - const auto LAYOUT = g_pConfigManager->getString("input:kb_layout"); - const auto VARIANT = g_pConfigManager->getString("input:kb_variant"); - const auto OPTIONS = g_pConfigManager->getString("input:kb_options"); + static auto* const PFILEPATH = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_file"); + static auto* const PRULES = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_rules"); + static auto* const PMODEL = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_model"); + static auto* const PLAYOUT = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_layout"); + static auto* const PVARIANT = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_variant"); + static auto* const POPTIONS = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("input:kb_options"); - xkb_rule_names rules = {.rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), .options = OPTIONS.c_str()}; - const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - FILE* const KEYMAPFILE = FILEPATH == "" ? NULL : fopen(absolutePath(FILEPATH, g_pConfigManager->configCurrentPath).c_str(), "r"); + const std::string FILEPATH = std::string{*PFILEPATH} == STRVAL_EMPTY ? "" : *PFILEPATH; + const std::string RULES = std::string{*PRULES} == STRVAL_EMPTY ? "" : *PRULES; + const std::string MODEL = std::string{*PMODEL} == STRVAL_EMPTY ? "" : *PMODEL; + const std::string LAYOUT = std::string{*PLAYOUT} == STRVAL_EMPTY ? "" : *PLAYOUT; + const std::string VARIANT = std::string{*PVARIANT} == STRVAL_EMPTY ? "" : *PVARIANT; + const std::string OPTIONS = std::string{*POPTIONS} == STRVAL_EMPTY ? "" : *POPTIONS; - auto PKEYMAP = KEYMAPFILE ? xkb_keymap_new_from_file(PCONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS) : - xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkb_rule_names rules = {.rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), .options = OPTIONS.c_str()}; + const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + FILE* const KEYMAPFILE = FILEPATH == "" ? NULL : fopen(absolutePath(FILEPATH, g_pConfigManager->configCurrentPath).c_str(), "r"); + + auto PKEYMAP = KEYMAPFILE ? xkb_keymap_new_from_file(PCONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS) : + xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (KEYMAPFILE) fclose(KEYMAPFILE); @@ -261,8 +287,7 @@ void CKeybindManager::switchToWindow(CWindow* PWINDOWTOCHANGETO) { bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard) { if (!g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { - m_dPressedKeycodes.clear(); - m_dPressedKeysyms.clear(); + m_dPressedKeys.clear(); return true; } @@ -279,7 +304,7 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard const auto KEYCODE = e->keycode + 8; // Because to xkbcommon it's +8 from libinput - const xkb_keysym_t keysym = xkb_state_key_get_one_sym(m_pXKBTranslationState, KEYCODE); + const xkb_keysym_t keysym = xkb_state_key_get_one_sym(pKeyboard->resolveBindsBySym ? pKeyboard->xkbTranslationState : m_pXKBTranslationState, KEYCODE); const xkb_keysym_t internalKeysym = xkb_state_key_get_one_sym(wlr_keyboard_from_input_device(pKeyboard->keyboard)->xkb_state, KEYCODE); if (handleInternalKeybinds(internalKeysym)) @@ -291,9 +316,16 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard m_uLastCode = KEYCODE; m_uLastMouseCode = 0; - bool mouseBindWasActive = ensureMouseBindState(); + bool mouseBindWasActive = ensureMouseBindState(); - bool found = false; + const auto KEY = SPressedKeyWithMods{ + .keysym = keysym, + .keycode = KEYCODE, + .modmaskAtPressTime = MODS, + .sent = true, + }; + + bool suppressEvent = false; if (e->state == WL_KEYBOARD_KEY_STATE_PRESSED) { // clean repeat if (m_pActiveKeybindEventSource) { @@ -302,16 +334,15 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard m_pActiveKeybind = nullptr; } - m_dPressedKeycodes.push_back(KEYCODE); - m_dPressedKeysyms.push_back(keysym); + m_dPressedKeys.push_back(KEY); - found = handleKeybinds(MODS, "", keysym, 0, true, e->time_msec) || found; + suppressEvent = handleKeybinds(MODS, KEY, true); - found = handleKeybinds(MODS, "", 0, KEYCODE, true, e->time_msec) || found; - - if (found) + if (suppressEvent) shadowKeybinds(keysym, KEYCODE); - } else if (e->state == WL_KEYBOARD_KEY_STATE_RELEASED) { + + m_dPressedKeys.back().sent = !suppressEvent; + } else { // key release // clean repeat if (m_pActiveKeybindEventSource) { wl_event_source_remove(m_pActiveKeybindEventSource); @@ -319,25 +350,35 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard m_pActiveKeybind = nullptr; } - m_dPressedKeycodes.erase(std::remove(m_dPressedKeycodes.begin(), m_dPressedKeycodes.end(), KEYCODE), m_dPressedKeycodes.end()); - m_dPressedKeysyms.erase(std::remove(m_dPressedKeysyms.begin(), m_dPressedKeysyms.end(), keysym), m_dPressedKeysyms.end()); - - found = handleKeybinds(MODS, "", keysym, 0, false, e->time_msec) || found; - - found = handleKeybinds(MODS, "", 0, KEYCODE, false, e->time_msec) || found; + bool foundInPressedKeys = false; + for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { + if (it->keycode == KEYCODE) { + suppressEvent = handleKeybinds(MODS, *it, false); + foundInPressedKeys = true; + suppressEvent = !it->sent; + it = m_dPressedKeys.erase(it); + } else { + ++it; + } + } + if (!foundInPressedKeys) { + Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys"); + // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy + suppressEvent = handleKeybinds(MODS, KEY, false); + } shadowKeybinds(); } - return !found && !mouseBindWasActive; + return !suppressEvent && !mouseBindWasActive; } bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); - static auto* const PDELAY = &g_pConfigManager->getConfigValuePtr("binds:scroll_event_delay")->intValue; + static auto* const PDELAY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:scroll_event_delay"); - if (m_tScrollTimer.getMillis() < *PDELAY) { + if (m_tScrollTimer.getMillis() < **PDELAY) { m_tScrollTimer.reset(); return true; // timer hasn't passed yet! } @@ -347,14 +388,14 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { bool found = false; if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_VERTICAL) { if (e->delta < 0) - found = handleKeybinds(MODS, "mouse_down", 0, 0, true, 0); + found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_down"}, true); else - found = handleKeybinds(MODS, "mouse_up", 0, 0, true, 0); + found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_up"}, true); } else if (e->source == WLR_AXIS_SOURCE_WHEEL && e->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) { if (e->delta < 0) - found = handleKeybinds(MODS, "mouse_left", 0, 0, true, 0); + found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_left"}, true); else - found = handleKeybinds(MODS, "mouse_right", 0, 0, true, 0); + found = handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_right"}, true); } if (found) @@ -366,26 +407,52 @@ bool CKeybindManager::onAxisEvent(wlr_pointer_axis_event* e) { bool CKeybindManager::onMouseEvent(wlr_pointer_button_event* e) { const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); - bool found = false; + bool suppressEvent = false; m_uLastMouseCode = e->button; m_uLastCode = 0; m_uTimeLastMs = e->time_msec; - bool mouseBindWasActive = ensureMouseBindState(); + bool mouseBindWasActive = ensureMouseBindState(); + + const auto KEY_NAME = "mouse:" + std::to_string(e->button); + + const auto KEY = SPressedKeyWithMods{ + .keyName = KEY_NAME, + .modmaskAtPressTime = MODS, + }; if (e->state == WLR_BUTTON_PRESSED) { - found = handleKeybinds(MODS, "mouse:" + std::to_string(e->button), 0, 0, true, 0); + m_dPressedKeys.push_back(KEY); - if (found) + suppressEvent = handleKeybinds(MODS, KEY, true); + + if (suppressEvent) shadowKeybinds(); + + m_dPressedKeys.back().sent = !suppressEvent; } else { - found = handleKeybinds(MODS, "mouse:" + std::to_string(e->button), 0, 0, false, 0); + bool foundInPressedKeys = false; + for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { + if (it->keyName == KEY_NAME) { + suppressEvent = handleKeybinds(MODS, *it, false); + foundInPressedKeys = true; + suppressEvent = !it->sent; + it = m_dPressedKeys.erase(it); + } else { + ++it; + } + } + if (!foundInPressedKeys) { + Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys (2)"); + // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy + suppressEvent = handleKeybinds(MODS, KEY, false); + } shadowKeybinds(); } - return !found && !mouseBindWasActive; + return !suppressEvent && !mouseBindWasActive; } void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) { @@ -397,15 +464,15 @@ void CKeybindManager::resizeWithBorder(wlr_pointer_button_event* e) { } void CKeybindManager::onSwitchEvent(const std::string& switchName) { - handleKeybinds(0, "switch:" + switchName, 0, 0, true, 0); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:" + switchName}, true); } void CKeybindManager::onSwitchOnEvent(const std::string& switchName) { - handleKeybinds(0, "switch:on:" + switchName, 0, 0, true, 0); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:on:" + switchName}, true); } void CKeybindManager::onSwitchOffEvent(const std::string& switchName) { - handleKeybinds(0, "switch:off:" + switchName, 0, 0, true, 0); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true); } int repeatKeyHandler(void* data) { @@ -424,12 +491,21 @@ int repeatKeyHandler(void* data) { return 0; } -bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& key, const xkb_keysym_t& keysym, const int& keycode, bool pressed, uint32_t time) { +bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) { bool found = false; if (g_pCompositor->m_sSeat.exclusiveClient) Debug::log(LOG, "Keybind handling only locked (inhibitor)"); + if (!m_lShortcutInhibitors.empty()) { + for (auto& i : m_lShortcutInhibitors) { + if (i.pWlrInhibitor->surface == g_pCompositor->m_pLastFocus) { + Debug::log(LOG, "Keybind handling is disabled due to an inhibitor for surface {:x}", (uintptr_t)i.pWlrInhibitor->surface); + return false; + } + } + } + for (auto& k : m_lKeybinds) { const bool SPECIALDISPATCHER = k.handler == "global" || k.handler == "pass" || k.handler == "mouse"; const bool SPECIALTRIGGERED = @@ -441,22 +517,31 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& ((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap || k.shadowed)) continue; - if (!key.empty()) { - if (key != k.key) + if (!key.keyName.empty()) { + if (key.keyName != k.key) continue; - } else if (k.keycode != -1) { - if (keycode != k.keycode) + } else if (k.keycode != 0) { + if (key.keycode != k.keycode) continue; } else { - if (keysym == 0) - continue; // this is a keycode check run - // oMg such performance hit!!11! // this little maneouver is gonna cost us 4µs - const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + + if (KBKEY == 0) { + // Keysym failed to resolve from the key name of the the currently iterated bind. + // This happens for names such as `switch:off:Lid Switch` as well as some keys + // (such as yen and ro). + // + // We can't let compare a 0-value with currently pressed key below, + // because if this key also have no keysym (i.e. key.keysym == 0) it will incorrectly trigger the + // currently iterated bind. That's confirmed to be happening with yen and ro keys. + continue; + } + const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); - if (keysym != KBKEY && keysym != KBKEYUPPER) + if (key.keysym != KBKEY && key.keysym != KBKEYUPPER) continue; } @@ -468,12 +553,24 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& continue; } - if (!pressed && !k.release && !SPECIALDISPATCHER) { - if (k.nonConsuming) - continue; + if (!pressed) { + // Require mods to be matching when the key was first pressed. + if (key.modmaskAtPressTime != modmask && !k.ignoreMods) { + // Handle properly `bindr` where a key is itself a bind mod for example: + // "bindr = SUPER, SUPER_L, exec, $launcher". + // This needs to be handled separately for the above case, because `key.modmaskAtPressTime` is set + // from currently pressed keys as programs see them, but it doesn't yet include the currently + // pressed mod key, which is still being handled internally. + if (keycodeToModifier(key.keycode) == key.modmaskAtPressTime) + continue; - found = true; // suppress the event - continue; + } else if (!k.release && !SPECIALDISPATCHER) { + if (k.nonConsuming) + continue; + + found = true; // suppress the event + continue; + } } const auto DISPATCHER = m_mDispatchers.find(k.mouse ? "mouse" : k.handler); @@ -488,7 +585,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& Debug::log(ERR, "Invalid handler in a keybind! (handler {} does not exist)", k.handler); } else { // call the dispatcher - Debug::log(LOG, "Keybind triggered, calling dispatcher ({}, {}, {})", modmask, key, keysym); + Debug::log(LOG, "Keybind triggered, calling dispatcher ({}, {}, {})", modmask, key.keyName, key.keysym); m_iPassPressed = (int)pressed; @@ -521,7 +618,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string& return found; } -void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const int& doesntHaveCode) { +void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint32_t doesntHaveCode) { // shadow disables keybinds after one has been triggered for (auto& k : m_lKeybinds) { @@ -534,22 +631,20 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const int& const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); - for (auto& pk : m_dPressedKeysyms) { - if ((pk == KBKEY || pk == KBKEYUPPER)) { + for (auto& pk : m_dPressedKeys) { + if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) { shadow = true; - if (pk == doesntHave && doesntHave != 0) { + if (pk.keysym == doesntHave && doesntHave != 0) { shadow = false; break; } } - } - for (auto& pk : m_dPressedKeycodes) { - if (pk == k.keycode) { + if (pk.keycode != 0 && pk.keycode == k.keycode) { shadow = true; - if (pk == doesntHaveCode && doesntHaveCode != 0 && doesntHaveCode != -1) { + if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) { shadow = false; break; } @@ -730,7 +825,7 @@ void CKeybindManager::clearKeybinds() { void CKeybindManager::toggleActiveFloating(std::string args) { CWindow* PWINDOW = nullptr; - if (args != "" && args != "active" && args.length() > 1) + if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else PWINDOW = g_pCompositor->m_pLastWindow; @@ -797,9 +892,9 @@ void CKeybindManager::changeworkspace(std::string args) { // Workspace_back_and_forth being enabled means that an attempt to switch to // the current workspace will instead switch to the previous. - static auto* const PBACKANDFORTH = &g_pConfigManager->getConfigValuePtr("binds:workspace_back_and_forth")->intValue; - static auto* const PALLOWWORKSPACECYCLES = &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue; - static auto* const PWORKSPACECENTERON = &g_pConfigManager->getConfigValuePtr("binds:workspace_center_on")->intValue; + static auto* const PBACKANDFORTH = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:workspace_back_and_forth"); + static auto* const PALLOWWORKSPACECYCLES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles"); + static auto* const PWORKSPACECENTERON = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:workspace_center_on"); const auto PMONITOR = g_pCompositor->m_pLastMonitor; @@ -834,7 +929,7 @@ void CKeybindManager::changeworkspace(std::string args) { const bool BISWORKSPACECURRENT = workspaceToChangeTo == PCURRENTWORKSPACE->m_iID; - if (BISWORKSPACECURRENT && (!(*PBACKANDFORTH || EXPLICITPREVIOUS) || PCURRENTWORKSPACE->m_sPrevWorkspace.iID == -1)) + if (BISWORKSPACECURRENT && (!(**PBACKANDFORTH || EXPLICITPREVIOUS) || PCURRENTWORKSPACE->m_sPrevWorkspace.iID == -1)) return; g_pInputManager->unconstrainMouse(); @@ -866,14 +961,14 @@ void CKeybindManager::changeworkspace(std::string args) { Vector2D middle = PMONITORWORKSPACEOWNER->middle(); if (const auto PLAST = pWorkspaceToChangeTo->getLastFocusedWindow(); PLAST) { g_pCompositor->focusWindow(PLAST); - if (*PWORKSPACECENTERON == 1) + if (**PWORKSPACECENTERON == 1) middle = PLAST->middle(); } g_pCompositor->warpCursorTo(middle); } if (BISWORKSPACECURRENT) { - if (*PALLOWWORKSPACECYCLES) + if (**PALLOWWORKSPACECYCLES) pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); else if (!EXPLICITPREVIOUS) pWorkspaceToChangeTo->rememberPrevWorkspace(nullptr); @@ -897,6 +992,9 @@ void CKeybindManager::fullscreenActive(std::string args) { if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) return; + PWINDOW->m_bDontSendFullscreen = false; + if (args == "2") + PWINDOW->m_bDontSendFullscreen = true; g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, args == "1" ? FULLSCREEN_MAXIMIZED : FULLSCREEN_FULL); } @@ -931,7 +1029,7 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) { auto pWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID); CMonitor* pMonitor = nullptr; const auto POLDWS = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID); - static auto* const PALLOWWORKSPACECYCLES = &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue; + static auto* const PALLOWWORKSPACECYCLES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles"); g_pHyprRenderer->damageWindow(PWINDOW); @@ -957,7 +1055,7 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) { g_pCompositor->focusWindow(PWINDOW); g_pCompositor->warpCursorTo(PWINDOW->middle()); - if (*PALLOWWORKSPACECYCLES) + if (**PALLOWWORKSPACECYCLES) pWorkspace->rememberPrevWorkspace(POLDWS); } @@ -1000,14 +1098,16 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } - if (const auto PATCOORDS = g_pCompositor->vectorToWindowIdeal(OLDMIDDLE); PATCOORDS && PATCOORDS != PWINDOW) - g_pCompositor->focusWindow(PATCOORDS); - else - g_pInputManager->refocus(); + if (PWINDOW == g_pCompositor->m_pLastWindow) { + if (const auto PATCOORDS = g_pCompositor->vectorToWindowUnified(OLDMIDDLE, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING, PWINDOW); PATCOORDS) + g_pCompositor->focusWindow(PATCOORDS); + else + g_pInputManager->refocus(); + } } void CKeybindManager::moveFocusTo(std::string args) { - static auto* const PFULLCYCLE = &g_pConfigManager->getConfigValuePtr("binds:movefocus_cycles_fullscreen")->intValue; + static auto* const PFULLCYCLE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:movefocus_cycles_fullscreen"); char arg = args[0]; if (!isDirection(args)) { @@ -1024,7 +1124,7 @@ void CKeybindManager::moveFocusTo(std::string args) { // remove constraints g_pInputManager->unconstrainMouse(); - const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->m_bIsFullscreen ? + const auto PWINDOWTOCHANGETO = **PFULLCYCLE && PLASTWINDOW->m_bIsFullscreen ? (arg == 'd' || arg == 'b' || arg == 'r' ? g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true) : g_pCompositor->getPrevWindowOnWorkspace(PLASTWINDOW, true)) : g_pCompositor->getWindowInDirection(PLASTWINDOW, arg); @@ -1039,8 +1139,8 @@ void CKeybindManager::moveFocusTo(std::string args) { if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg))) return; - static auto* const PNOFALLBACK = &g_pConfigManager->getConfigValuePtr("general:no_focus_fallback")->intValue; - if (*PNOFALLBACK) + static auto* const PNOFALLBACK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:no_focus_fallback"); + if (**PNOFALLBACK) return; Debug::log(LOG, "No monitor found in direction {}, falling back to next window on current workspace", arg); @@ -1218,6 +1318,21 @@ void CKeybindManager::toggleSplit(std::string args) { g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "togglesplit"); } +void CKeybindManager::swapSplit(std::string args) { + SLayoutMessageHeader header; + header.pWindow = g_pCompositor->m_pLastWindow; + + if (!header.pWindow) + return; + + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(header.pWindow->m_iWorkspaceID); + + if (PWORKSPACE->m_bHasFullscreenWindow) + return; + + g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "swapsplit"); +} + void CKeybindManager::alterSplitRatio(std::string args) { std::optional splitResult; bool exact = false; @@ -1385,20 +1500,24 @@ void CKeybindManager::renameWorkspace(std::string args) { } void CKeybindManager::exitHyprland(std::string argz) { - g_pCompositor->cleanup(); + g_pInputManager->m_bExitTriggered = true; } void CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) { CMonitor* PMONITOR = g_pCompositor->getMonitorFromString(args); - if (!PMONITOR) + if (!PMONITOR) { + Debug::log(ERR, "Ignoring moveCurrentWorkspaceToMonitor: monitor doesnt exist"); return; + } // get the current workspace const auto PCURRENTWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace); - if (!PCURRENTWORKSPACE) + if (!PCURRENTWORKSPACE) { + Debug::log(ERR, "moveCurrentWorkspaceToMonitor invalid workspace!"); return; + } g_pCompositor->moveWorkspaceToMonitor(PCURRENTWORKSPACE, PMONITOR); } @@ -1435,9 +1554,51 @@ void CKeybindManager::moveWorkspaceToMonitor(std::string args) { g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PMONITOR); } +void CKeybindManager::focusWorkspaceOnCurrentMonitor(std::string args) { + std::string workspaceName; + const int WORKSPACEID = getWorkspaceIDFromString(args, workspaceName); + + if (WORKSPACEID == WORKSPACE_INVALID) { + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor invalid workspace!"); + return; + } + + const auto PCURRMONITOR = g_pCompositor->m_pLastMonitor; + + if (!PCURRMONITOR) { + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor monitor doesn't exist!"); + return; + } + + auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); + + if (!PWORKSPACE) { + PWORKSPACE = g_pCompositor->createNewWorkspace(WORKSPACEID, PCURRMONITOR->ID); + // we can skip the moving, since it's already on the current monitor + changeworkspace(PWORKSPACE->getConfigName()); + return; + } + + if (PWORKSPACE->m_iMonitorID != PCURRMONITOR->ID) { + const auto POLDMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID); + if (!POLDMONITOR) { // wat + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!"); + return; + } + if (POLDMONITOR->activeWorkspace == WORKSPACEID) { + g_pCompositor->swapActiveWorkspaces(POLDMONITOR, PCURRMONITOR); + return; + } else { + g_pCompositor->moveWorkspaceToMonitor(PWORKSPACE, PCURRMONITOR, true); + } + } + + changeworkspace(PWORKSPACE->getConfigName()); +} + void CKeybindManager::toggleSpecialWorkspace(std::string args) { - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; + static auto* const PFOLLOWMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:follow_mouse"); std::string workspaceName = ""; int workspaceID = getWorkspaceIDFromString("special:" + args, workspaceName); @@ -1448,7 +1609,7 @@ void CKeybindManager::toggleSpecialWorkspace(std::string args) { } bool requestedWorkspaceIsAlreadyOpen = false; - const auto PMONITOR = *PFOLLOWMOUSE == 1 ? g_pCompositor->getMonitorFromCursor() : g_pCompositor->m_pLastMonitor; + const auto PMONITOR = **PFOLLOWMOUSE == 1 ? g_pCompositor->getMonitorFromCursor() : g_pCompositor->m_pLastMonitor; int specialOpenOnMonitor = PMONITOR->specialWorkspaceID; for (auto& m : g_pCompositor->m_vMonitors) { @@ -1480,7 +1641,7 @@ void CKeybindManager::forceRendererReload(std::string args) { if (!m->output) continue; - auto rule = g_pConfigManager->getMonitorRuleFor(m->szName, m->szDescription); + auto rule = g_pConfigManager->getMonitorRuleFor(*m); if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule, true)) { overAgain = true; break; @@ -1596,17 +1757,40 @@ void CKeybindManager::focusWindow(std::string regexp) { Debug::log(LOG, "Focusing to window name: {}", PWINDOW->m_szTitle); + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID); + if (!PWORKSPACE) { + Debug::log(ERR, "BUG THIS: null workspace in focusWindow"); + return; + } + if (g_pCompositor->m_pLastMonitor->activeWorkspace != PWINDOW->m_iWorkspaceID) { Debug::log(LOG, "Fake executing workspace to move focus"); - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID); - if (!PWORKSPACE) { - Debug::log(ERR, "BUG THIS: null workspace in focusWindow"); - return; - } changeworkspace(PWORKSPACE->getConfigName()); } - g_pCompositor->focusWindow(PWINDOW); + if (PWORKSPACE->m_bHasFullscreenWindow) { + const auto FSWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); + const auto FSMODE = PWORKSPACE->m_efFullscreenMode; + + if (PWINDOW->m_bIsFloating) { + // don't make floating implicitly fs + if (!PWINDOW->m_bCreatedOverFullscreen) { + g_pCompositor->changeWindowZOrder(PWINDOW, true); + g_pCompositor->updateFullscreenFadeOnWorkspace(PWORKSPACE); + } + + g_pCompositor->focusWindow(PWINDOW); + } else { + if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned) + g_pCompositor->setWindowFullscreen(FSWINDOW, false, FULLSCREEN_FULL); + + g_pCompositor->focusWindow(PWINDOW); + + if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned) + g_pCompositor->setWindowFullscreen(PWINDOW, true, FSMODE); + } + } else + g_pCompositor->focusWindow(PWINDOW); g_pCompositor->warpCursorTo(PWINDOW->middle()); } @@ -1742,11 +1926,11 @@ void CKeybindManager::dpms(std::string arg) { if (!port.empty() && m->szName != port) continue; - wlr_output_enable(m->output, enable); + wlr_output_state_set_enabled(m->state.wlr(), enable); m->dpmsStatus = enable; - if (!wlr_output_commit(m->output)) { + if (!m->state.commit()) { Debug::log(ERR, "Couldn't commit output {}", m->szName); } @@ -1808,7 +1992,7 @@ void CKeybindManager::pinActive(std::string args) { CWindow* PWINDOW = nullptr; - if (args != "" && args != "active" && args.length() > 1) + if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else PWINDOW = g_pCompositor->m_pLastWindow; @@ -1829,7 +2013,7 @@ void CKeybindManager::pinActive(std::string args) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID); - PWORKSPACE->m_pLastFocusedWindow = g_pCompositor->vectorToWindowTiled(g_pInputManager->getMouseCoordsInternal()); + PWORKSPACE->m_pLastFocusedWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS); } void CKeybindManager::mouse(std::string args) { @@ -1841,7 +2025,7 @@ void CKeybindManager::mouse(std::string args) { g_pKeybindManager->m_bIsMouseBindActive = true; const auto mouseCoords = g_pInputManager->getMouseCoordsInternal(); - CWindow* pWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords); + CWindow* pWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (pWindow && !pWindow->m_bIsFullscreen) pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_START, mouseCoords); @@ -1864,7 +2048,8 @@ void CKeybindManager::mouse(std::string args) { if (PRESSED) { g_pKeybindManager->m_bIsMouseBindActive = true; - g_pInputManager->currentlyDraggedWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal()); + g_pInputManager->currentlyDraggedWindow = + g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); try { switch (std::stoi(ARGS[1])) { @@ -1959,8 +2144,8 @@ void CKeybindManager::moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDi g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property! - static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue; - pWindowInDirection = *USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail(); + static const auto* USECURRPOS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:insert_after_current"); + pWindowInDirection = **USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail(); pWindowInDirection->insertWindowToGroup(pWindow); pWindowInDirection->setGroupCurrent(pWindow); @@ -1974,7 +2159,7 @@ void CKeybindManager::moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDi } void CKeybindManager::moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir) { - static auto* const BFOCUSREMOVEDWINDOW = &g_pConfigManager->getConfigValuePtr("group:focus_removed_window")->intValue; + static auto* const BFOCUSREMOVEDWINDOW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:focus_removed_window"); const auto PWINDOWPREV = pWindow->getGroupPrevious(); eDirection direction; @@ -2001,7 +2186,7 @@ void CKeybindManager::moveWindowOutOfGroup(CWindow* pWindow, const std::string& g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV; } - if (*BFOCUSREMOVEDWINDOW) { + if (**BFOCUSREMOVEDWINDOW) { g_pCompositor->focusWindow(pWindow); g_pCompositor->warpCursorTo(pWindow->middle()); } else { @@ -2013,9 +2198,9 @@ void CKeybindManager::moveWindowOutOfGroup(CWindow* pWindow, const std::string& void CKeybindManager::moveIntoGroup(std::string args) { char arg = args[0]; - static auto* const PIGNOREGROUPLOCK = &g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock")->intValue; + static auto* const PIGNOREGROUPLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock"); - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) + if (!**PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) return; if (!isDirection(args)) { @@ -2034,19 +2219,24 @@ void CKeybindManager::moveIntoGroup(std::string args) { return; // Do not move window into locked group if binds:ignore_group_lock is false - if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || (PWINDOW->m_sGroupData.pNextWindow && PWINDOW->getGroupHead()->m_sGroupData.locked))) + if (!**PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || (PWINDOW->m_sGroupData.pNextWindow && PWINDOW->getGroupHead()->m_sGroupData.locked))) return; moveWindowIntoGroup(PWINDOW, PWINDOWINDIR); } void CKeybindManager::moveOutOfGroup(std::string args) { - static auto* const PIGNOREGROUPLOCK = &g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock")->intValue; + static auto* const PIGNOREGROUPLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock"); - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) + if (!**PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) return; - const auto PWINDOW = g_pCompositor->m_pLastWindow; + CWindow* PWINDOW = nullptr; + + if (args != "active" && args.length() > 1) + PWINDOW = g_pCompositor->getWindowByRegex(args); + else + PWINDOW = g_pCompositor->m_pLastWindow; if (!PWINDOW || !PWINDOW->m_sGroupData.pNextWindow) return; @@ -2057,7 +2247,7 @@ void CKeybindManager::moveOutOfGroup(std::string args) { void CKeybindManager::moveWindowOrGroup(std::string args) { char arg = args[0]; - static auto* const PIGNOREGROUPLOCK = &g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock")->intValue; + static auto* const PIGNOREGROUPLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock"); if (!isDirection(args)) { Debug::log(ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); @@ -2068,7 +2258,7 @@ void CKeybindManager::moveWindowOrGroup(std::string args) { if (!PWINDOW || PWINDOW->m_bIsFullscreen) return; - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) { + if (!**PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) { g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); return; } @@ -2081,32 +2271,36 @@ void CKeybindManager::moveWindowOrGroup(std::string args) { // note: PWINDOWINDIR is not null implies !PWINDOW->m_bIsFloating if (PWINDOWINDIR && PWINDOWINDIR->m_sGroupData.pNextWindow) { // target is group - if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || ISWINDOWGROUPLOCKED || PWINDOW->m_sGroupData.deny)) { + if (!**PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || ISWINDOWGROUPLOCKED || PWINDOW->m_sGroupData.deny)) { g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); g_pCompositor->warpCursorTo(PWINDOW->middle()); } else moveWindowIntoGroup(PWINDOW, PWINDOWINDIR); } else if (PWINDOWINDIR) { // target is regular window - if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_eGroupRules & GROUP_SET_ALWAYS)) { + if ((!**PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_eGroupRules & GROUP_SET_ALWAYS)) { g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); g_pCompositor->warpCursorTo(PWINDOW->middle()); } else moveWindowOutOfGroup(PWINDOW, args); - } else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) // no target window + } else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) { // no target window moveWindowOutOfGroup(PWINDOW, args); + } else if (!PWINDOWINDIR && !ISWINDOWGROUP) { // no target in dir and not in group + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); + g_pCompositor->warpCursorTo(PWINDOW->middle()); + } g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); } void CKeybindManager::setIgnoreGroupLock(std::string args) { - static auto* const BIGNOREGROUPLOCK = &g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock")->intValue; + static auto* const BIGNOREGROUPLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock"); if (args == "toggle") - *BIGNOREGROUPLOCK = !*BIGNOREGROUPLOCK; + **BIGNOREGROUPLOCK = !*BIGNOREGROUPLOCK; else - *BIGNOREGROUPLOCK = args == "on"; + **BIGNOREGROUPLOCK = args == "on"; - g_pEventManager->postEvent(SHyprIPCEvent{"ignoregrouplock", std::to_string(*BIGNOREGROUPLOCK)}); + g_pEventManager->postEvent(SHyprIPCEvent{"ignoregrouplock", std::to_string(**BIGNOREGROUPLOCK)}); } void CKeybindManager::denyWindowFromGroup(std::string args) { diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 73997dd6..edd9ede2 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -12,7 +12,7 @@ class CPluginSystem; struct SKeybind { std::string key = ""; - int keycode = -1; + uint32_t keycode = 0; uint32_t modmask = 0; std::string handler = ""; std::string arg = ""; @@ -36,6 +36,14 @@ enum eFocusWindowMode { MODE_PID }; +struct SPressedKeyWithMods { + std::string keyName = ""; + xkb_keysym_t keysym = 0; + uint32_t keycode = 0; + uint32_t modmaskAtPressTime = 0; + bool sent = false; +}; + class CKeybindManager { public: CKeybindManager(); @@ -51,8 +59,9 @@ class CKeybindManager { void addKeybind(SKeybind); void removeKeybind(uint32_t, const std::string&); uint32_t stringToModMask(std::string); + uint32_t keycodeToModifier(xkb_keycode_t); void clearKeybinds(); - void shadowKeybinds(const xkb_keysym_t& doesntHave = 0, const int& doesntHaveCode = 0); + void shadowKeybinds(const xkb_keysym_t& doesntHave = 0, const uint32_t doesntHaveCode = 0); std::unordered_map> m_mDispatchers; @@ -61,40 +70,40 @@ class CKeybindManager { bool m_bGroupsLocked = false; std::list m_lKeybinds; + std::list m_lShortcutInhibitors; private: - std::deque m_dPressedKeysyms; - std::deque m_dPressedKeycodes; + std::deque m_dPressedKeys; - inline static std::string m_szCurrentSelectedSubmap = ""; + inline static std::string m_szCurrentSelectedSubmap = ""; - SKeybind* m_pActiveKeybind = nullptr; + SKeybind* m_pActiveKeybind = nullptr; - uint32_t m_uTimeLastMs = 0; - uint32_t m_uLastCode = 0; - uint32_t m_uLastMouseCode = 0; + uint32_t m_uTimeLastMs = 0; + uint32_t m_uLastCode = 0; + uint32_t m_uLastMouseCode = 0; - bool m_bIsMouseBindActive = false; - std::vector m_vPressedSpecialBinds; + bool m_bIsMouseBindActive = false; + std::vector m_vPressedSpecialBinds; - int m_iPassPressed = -1; // used for pass + int m_iPassPressed = -1; // used for pass - CTimer m_tScrollTimer; + CTimer m_tScrollTimer; - bool handleKeybinds(const uint32_t&, const std::string&, const xkb_keysym_t&, const int&, bool, uint32_t); + bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool); - bool handleInternalKeybinds(xkb_keysym_t); - bool handleVT(xkb_keysym_t); + bool handleInternalKeybinds(xkb_keysym_t); + bool handleVT(xkb_keysym_t); - xkb_state* m_pXKBTranslationState = nullptr; + xkb_state* m_pXKBTranslationState = nullptr; - void updateXKBTranslationState(); - bool ensureMouseBindState(); + void updateXKBTranslationState(); + bool ensureMouseBindState(); - static bool tryMoveFocusToMonitor(CMonitor* monitor); - static void moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir = ""); - static void moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDirection); - static void switchToWindow(CWindow* PWINDOWTOCHANGETO); + static bool tryMoveFocusToMonitor(CMonitor* monitor); + static void moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir = ""); + static void moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDirection); + static void switchToWindow(CWindow* PWINDOWTOCHANGETO); // -------------- Dispatchers -------------- // static void killActive(std::string); @@ -119,6 +128,7 @@ class CKeybindManager { static void alterSplitRatio(std::string); static void focusMonitor(std::string); static void toggleSplit(std::string); + static void swapSplit(std::string); static void moveCursorToCorner(std::string); static void moveCursor(std::string); static void workspaceOpt(std::string); @@ -126,6 +136,7 @@ class CKeybindManager { static void exitHyprland(std::string); static void moveCurrentWorkspaceToMonitor(std::string); static void moveWorkspaceToMonitor(std::string); + static void focusWorkspaceOnCurrentMonitor(std::string); static void toggleSpecialWorkspace(std::string); static void forceRendererReload(std::string); static void resizeActive(std::string); diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp index 9774490b..89c08566 100644 --- a/src/managers/SessionLockManager.cpp +++ b/src/managers/SessionLockManager.cpp @@ -44,9 +44,9 @@ static void handleSurfaceDestroy(void* owner, void* data) { void CSessionLockManager::onNewSessionLock(wlr_session_lock_v1* pWlrLock) { - static auto* const PALLOWRELOCK = &g_pConfigManager->getConfigValuePtr("misc:allow_session_lock_restore")->intValue; + static auto* const PALLOWRELOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:allow_session_lock_restore"); - if (m_sSessionLock.active && (!*PALLOWRELOCK || m_sSessionLock.pWlrLock)) { + if (m_sSessionLock.active && (!**PALLOWRELOCK || m_sSessionLock.pWlrLock)) { Debug::log(LOG, "Attempted to lock a locked session!"); wlr_session_lock_v1_destroy(pWlrLock); return; @@ -75,6 +75,8 @@ void CSessionLockManager::onNewSessionLock(wlr_session_lock_v1* pWlrLock) { PSURFACE->pWlrLockSurface = PWLRSURFACE; PSURFACE->iMonitorID = PMONITOR->ID; + g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PSURFACE->pWlrLockSurface->surface, PMONITOR->scale); + wlr_session_lock_surface_v1_configure(PWLRSURFACE, PMONITOR->vecSize.x, PMONITOR->vecSize.y); PSURFACE->hyprListener_map.initCallback(&PWLRSURFACE->surface->events.map, &handleSurfaceMap, PSURFACE, "SSessionLockSurface"); diff --git a/src/managers/ThreadManager.cpp b/src/managers/ThreadManager.cpp index 060e69dd..ee58a8ce 100644 --- a/src/managers/ThreadManager.cpp +++ b/src/managers/ThreadManager.cpp @@ -7,9 +7,9 @@ int slowUpdate = 0; int handleTimer(void* data) { const auto PTM = (CThreadManager*)data; - static auto* const PDISABLECFGRELOAD = &g_pConfigManager->getConfigValuePtr("misc:disable_autoreload")->intValue; + static auto* const PDISABLECFGRELOAD = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:disable_autoreload"); - if (*PDISABLECFGRELOAD != 1) + if (**PDISABLECFGRELOAD != 1) g_pConfigManager->tick(); wl_event_source_timer_update(PTM->m_esConfigTimer, 1000); @@ -18,8 +18,6 @@ int handleTimer(void* data) { } CThreadManager::CThreadManager() { - HyprCtl::startHyprCtlSocket(); - m_esConfigTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, handleTimer, this); wl_event_source_timer_update(m_esConfigTimer, 1000); diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 63b8cca0..b46004e6 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -147,17 +147,19 @@ void CHyprXWaylandManager::sendCloseWindow(CWindow* pWindow) { void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, Vector2D size, bool force) { - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + size = size.clamp(Vector2D{0, 0}, Vector2D{std::numeric_limits::infinity(), std::numeric_limits::infinity()}); + // calculate pos // TODO: this should be decoupled from setWindowSize IMO Vector2D windowPos = pWindow->m_vRealPosition.vec(); if (pWindow->m_bIsX11 && PMONITOR) { windowPos = windowPos - PMONITOR->vecPosition; // normalize to monitor - if (*PXWLFORCESCALEZERO) + if (**PXWLFORCESCALEZERO) windowPos = windowPos * PMONITOR->scale; // scale if applicable windowPos = windowPos + PMONITOR->vecXWaylandPosition; // move to correct position for xwayland } @@ -170,7 +172,7 @@ void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, Vector2D size, bool f pWindow->m_fX11SurfaceScaledBy = 1.f; - if (*PXWLFORCESCALEZERO && pWindow->m_bIsX11 && PMONITOR) { + if (**PXWLFORCESCALEZERO && pWindow->m_bIsX11 && PMONITOR) { size = size * PMONITOR->scale; pWindow->m_fX11SurfaceScaledBy = PMONITOR->scale; } @@ -315,12 +317,12 @@ Vector2D CHyprXWaylandManager::getMaxSizeForWindow(CWindow* pWindow) { Vector2D CHyprXWaylandManager::xwaylandToWaylandCoords(const Vector2D& coord) { - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); CMonitor* pMonitor = nullptr; double bestDistance = __FLT_MAX__; for (auto& m : g_pCompositor->m_vMonitors) { - const auto SIZ = *PXWLFORCESCALEZERO ? m->vecTransformedSize : m->vecSize; + const auto SIZ = **PXWLFORCESCALEZERO ? m->vecTransformedSize : m->vecSize; double distance = vecToRectDistanceSquared(coord, {m->vecXWaylandPosition.x, m->vecXWaylandPosition.y}, {m->vecXWaylandPosition.x + SIZ.x - 1, m->vecXWaylandPosition.y + SIZ.y - 1}); @@ -337,7 +339,7 @@ Vector2D CHyprXWaylandManager::xwaylandToWaylandCoords(const Vector2D& coord) { // get local coords Vector2D result = coord - pMonitor->vecXWaylandPosition; // if scaled, unscale - if (*PXWLFORCESCALEZERO) + if (**PXWLFORCESCALEZERO) result = result / pMonitor->scale; // add pos result = result + pMonitor->vecPosition; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 207df738..2c8c2d7c 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -16,20 +16,20 @@ CInputManager::~CInputManager() { } void CInputManager::onMouseMoved(wlr_pointer_motion_event* e) { - static auto* const PSENS = &g_pConfigManager->getConfigValuePtr("general:sensitivity")->floatValue; - static auto* const PNOACCEL = &g_pConfigManager->getConfigValuePtr("input:force_no_accel")->intValue; - static auto* const PSENSTORAW = &g_pConfigManager->getConfigValuePtr("general:apply_sens_to_raw")->intValue; + static auto* const PSENS = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("general:sensitivity"); + static auto* const PNOACCEL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:force_no_accel"); + static auto* const PSENSTORAW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:apply_sens_to_raw"); - const auto DELTA = *PNOACCEL == 1 ? Vector2D(e->unaccel_dx, e->unaccel_dy) : Vector2D(e->delta_x, e->delta_y); + const auto DELTA = **PNOACCEL == 1 ? Vector2D(e->unaccel_dx, e->unaccel_dy) : Vector2D(e->delta_x, e->delta_y); - if (*PSENSTORAW == 1) - wlr_relative_pointer_manager_v1_send_relative_motion(g_pCompositor->m_sWLRRelPointerMgr, g_pCompositor->m_sSeat.seat, (uint64_t)e->time_msec * 1000, DELTA.x * *PSENS, - DELTA.y * *PSENS, e->unaccel_dx * *PSENS, e->unaccel_dy * *PSENS); + if (**PSENSTORAW == 1) + wlr_relative_pointer_manager_v1_send_relative_motion(g_pCompositor->m_sWLRRelPointerMgr, g_pCompositor->m_sSeat.seat, (uint64_t)e->time_msec * 1000, DELTA.x * **PSENS, + DELTA.y * **PSENS, e->unaccel_dx * **PSENS, e->unaccel_dy * **PSENS); else wlr_relative_pointer_manager_v1_send_relative_motion(g_pCompositor->m_sWLRRelPointerMgr, g_pCompositor->m_sSeat.seat, (uint64_t)e->time_msec * 1000, DELTA.x, DELTA.y, e->unaccel_dx, e->unaccel_dy); - wlr_cursor_move(g_pCompositor->m_sWLRCursor, &e->pointer->base, DELTA.x * *PSENS, DELTA.y * *PSENS); + wlr_cursor_move(g_pCompositor->m_sWLRCursor, &e->pointer->base, DELTA.x * **PSENS, DELTA.y * **PSENS); mouseMoveUnified(e->time_msec); @@ -73,17 +73,17 @@ void CInputManager::sendMotionEventsToFocused() { } void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; - static auto* const PMOUSEREFOCUS = &g_pConfigManager->getConfigValuePtr("input:mouse_refocus")->intValue; - static auto* const PMOUSEDPMS = &g_pConfigManager->getConfigValuePtr("misc:mouse_move_enables_dpms")->intValue; - static auto* const PFOLLOWONDND = &g_pConfigManager->getConfigValuePtr("misc:always_follow_on_dnd")->intValue; - static auto* const PFLOATBEHAVIOR = &g_pConfigManager->getConfigValuePtr("input:float_switch_override_focus")->intValue; - static auto* const PMOUSEFOCUSMON = &g_pConfigManager->getConfigValuePtr("misc:mouse_move_focuses_monitor")->intValue; - static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue; - static auto* const PRESIZECURSORICON = &g_pConfigManager->getConfigValuePtr("general:hover_icon_on_border")->intValue; - static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue; + static auto* const PFOLLOWMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:follow_mouse"); + static auto* const PMOUSEREFOCUS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:mouse_refocus"); + static auto* const PMOUSEDPMS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:mouse_move_enables_dpms"); + static auto* const PFOLLOWONDND = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:always_follow_on_dnd"); + static auto* const PFLOATBEHAVIOR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:float_switch_override_focus"); + static auto* const PMOUSEFOCUSMON = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:mouse_move_focuses_monitor"); + static auto* const PRESIZEONBORDER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:resize_on_border"); + static auto* const PRESIZECURSORICON = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:hover_icon_on_border"); + static auto* const PZOOMFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor"); - const auto FOLLOWMOUSE = *PFOLLOWONDND && m_sDrag.drag ? 1 : *PFOLLOWMOUSE; + const auto FOLLOWMOUSE = **PFOLLOWONDND && m_sDrag.drag ? 1 : **PFOLLOWMOUSE; m_pFoundSurfaceToFocus = nullptr; m_pFoundLSToFocus = nullptr; @@ -97,7 +97,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState) return; - if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) { + if (!g_pCompositor->m_bDPMSStateON && **PMOUSEDPMS) { // enable dpms g_pKeybindManager->dpms("on"); } @@ -121,7 +121,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (PMONITOR == nullptr) return; - if (*PZOOMFACTOR != 1.f) + if (**PZOOMFACTOR != 1.f) g_pHyprRenderer->damageMonitor(PMONITOR); if (!PMONITOR->solitaryClient && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0) @@ -220,7 +220,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal()); - if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor && (*PMOUSEFOCUSMON || refocus)) + if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor && (**PMOUSEFOCUSMON || refocus)) g_pCompositor->setActiveMonitor(PMONITOR); if (g_pSessionLockManager->isSessionLocked()) { @@ -261,7 +261,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { return; } - const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowIdeal(mouseCoords); + const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (PWINDOWIDEAL && ((PWINDOWIDEAL->m_bIsFloating && PWINDOWIDEAL->m_bCreatedOverFullscreen) /* floating over fullscreen */ @@ -282,19 +282,19 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) { if (PMONITOR->specialWorkspaceID) { - pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (pFoundWindow && !g_pCompositor->isWorkspaceSpecial(pFoundWindow->m_iWorkspaceID)) { pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); } } else { - pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!(pFoundWindow && pFoundWindow->m_bIsFloating && pFoundWindow->m_bCreatedOverFullscreen)) pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); } } else { - pFoundWindow = g_pCompositor->vectorToWindowIdeal(mouseCoords); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); } if (pFoundWindow) { @@ -315,11 +315,12 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface); - g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor); + if (g_pCompositor->m_pLastMonitor->output->software_cursor_locks > 0) + g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor); if (!foundSurface) { if (!m_bEmptyFocusCursorSet) { - if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { + if (**PRESIZEONBORDER && **PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { m_eBorderIconDirection = BORDERICON_NONE; unsetCursorImage(); } @@ -389,7 +390,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (pFoundWindow) { // change cursor icon if hovering over border - if (*PRESIZEONBORDER && *PRESIZECURSORICON) { + if (**PRESIZEONBORDER && **PRESIZECURSORICON) { if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) { setCursorIconOnBorder(pFoundWindow); } else if (m_eBorderIconDirection != BORDERICON_NONE) { @@ -399,7 +400,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (FOLLOWMOUSE != 1 && !refocus) { if (pFoundWindow != g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow && - ((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) { + ((pFoundWindow->m_bIsFloating && **PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && **PFLOATBEHAVIOR != 0))) { // enter if change floating style if (FOLLOWMOUSE != 3 && allowKeyboardRefocus) g_pCompositor->focusWindow(pFoundWindow, foundSurface); @@ -421,15 +422,23 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bLastFocusOnLS = false; return; // don't enter any new surfaces } else { - if (((FOLLOWMOUSE != 3 && allowKeyboardRefocus) && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus) { - m_pLastMouseFocus = pFoundWindow; - g_pCompositor->focusWindow(pFoundWindow, foundSurface); + if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus)) { + if (m_pLastMouseFocus != pFoundWindow || g_pCompositor->m_pLastWindow != pFoundWindow || g_pCompositor->m_pLastFocus != foundSurface || refocus) { + m_pLastMouseFocus = pFoundWindow; + + // TODO: this looks wrong. When over a popup, it constantly is switching. + // Temp fix until that's figured out. Otherwise spams windowrule lookups and other shit. + if (m_pLastMouseFocus != pFoundWindow || g_pCompositor->m_pLastWindow != pFoundWindow) + g_pCompositor->focusWindow(pFoundWindow, foundSurface); + else + g_pCompositor->focusSurface(foundSurface, pFoundWindow); + } } } m_bLastFocusOnLS = false; } else { - if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { + if (**PRESIZEONBORDER && **PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { m_eBorderIconDirection = BORDERICON_NONE; unsetCursorImage(); } @@ -486,11 +495,18 @@ void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_even if (!cursorImageUnlocked()) return; + Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e->surface); + if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client) { - m_sCursorSurfaceInfo.wlSurface.unassign(); + + if (e->surface != m_sCursorSurfaceInfo.wlSurface.wlr()) { + m_sCursorSurfaceInfo.wlSurface.unassign(); + + if (e->surface) + m_sCursorSurfaceInfo.wlSurface.assign(e->surface); + } if (e->surface) { - m_sCursorSurfaceInfo.wlSurface.assign(e->surface); m_sCursorSurfaceInfo.vHotspot = {e->hotspot_x, e->hotspot_y}; m_sCursorSurfaceInfo.hidden = false; } else { @@ -509,6 +525,8 @@ void CInputManager::processMouseRequest(wlr_cursor_shape_manager_v1_request_set_ if (!cursorImageUnlocked()) return; + Debug::log(LOG, "cursorImage request: shape {}", (uint32_t)e->shape); + if (e->seat_client == g_pCompositor->m_sSeat.seat->pointer_state.focused_client) { m_sCursorSurfaceInfo.wlSurface.unassign(); m_sCursorSurfaceInfo.vHotspot = {}; @@ -587,26 +605,26 @@ void CInputManager::setClickMode(eClickBehaviorMode mode) { void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) { // notify the keybind manager - static auto* const PPASSMOUSE = &g_pConfigManager->getConfigValuePtr("binds:pass_mouse_when_bound")->intValue; + static auto* const PPASSMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:pass_mouse_when_bound"); const auto PASS = g_pKeybindManager->onMouseEvent(e); - static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; - static auto* const PRESIZEONBORDER = &g_pConfigManager->getConfigValuePtr("general:resize_on_border")->intValue; - static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; - static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue; - const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0; + static auto* const PFOLLOWMOUSE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("input:follow_mouse"); + static auto* const PRESIZEONBORDER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:resize_on_border"); + static auto* const PBORDERSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:border_size"); + static auto* const PBORDERGRABEXTEND = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area"); + const auto BORDER_GRAB_AREA = **PRESIZEONBORDER ? **PBORDERSIZE + **PBORDERGRABEXTEND : 0; - if (!PASS && !*PPASSMOUSE) + if (!PASS && !**PPASSMOUSE) return; const auto mouseCoords = g_pInputManager->getMouseCoordsInternal(); - const auto w = g_pCompositor->vectorToWindowIdeal(mouseCoords); + const auto w = g_pCompositor->vectorToWindowUnified(mouseCoords, ALLOW_FLOATING | RESERVED_EXTENTS | INPUT_EXTENTS); if (w && !m_bLastFocusOnLS && w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e)) return; // clicking on border triggers resize // TODO detect click on LS properly - if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e->state == WLR_BUTTON_PRESSED) { + if (**PRESIZEONBORDER && !m_bLastFocusOnLS && e->state == WLR_BUTTON_PRESSED) { if (w && !w->m_bIsFullscreen) { const CBox real = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y}; const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA}; @@ -620,7 +638,7 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) { switch (e->state) { case WLR_BUTTON_PRESSED: - if (*PFOLLOWMOUSE == 3) // don't refocus on full loose + if (**PFOLLOWMOUSE == 3) // don't refocus on full loose break; if ((!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) /* No constraints */ @@ -645,15 +663,17 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) { } // notify app if we didnt handle it - if (g_pCompositor->doesSeatAcceptInput(g_pCompositor->m_pLastFocus)) { + if (g_pCompositor->doesSeatAcceptInput(g_pCompositor->m_pLastFocus)) wlr_seat_pointer_notify_button(g_pCompositor->m_sSeat.seat, e->time_msec, e->button, e->state); - } + + if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor && PMON) + g_pCompositor->setActiveMonitor(PMON); } void CInputManager::processMouseDownKill(wlr_pointer_button_event* e) { switch (e->state) { case WLR_BUTTON_PRESSED: { - const auto PWINDOW = g_pCompositor->vectorToWindowIdeal(getMouseCoordsInternal()); + const auto PWINDOW = g_pCompositor->vectorToWindowUnified(getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!PWINDOW) { Debug::log(ERR, "Cannot kill invalid window!"); @@ -673,9 +693,9 @@ void CInputManager::processMouseDownKill(wlr_pointer_button_event* e) { } void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) { - static auto* const PSCROLLFACTOR = &g_pConfigManager->getConfigValuePtr("input:touchpad:scroll_factor")->floatValue; + static auto* const PSCROLLFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("input:touchpad:scroll_factor"); - auto factor = (*PSCROLLFACTOR <= 0.f || e->source != WLR_AXIS_SOURCE_FINGER ? 1.f : *PSCROLLFACTOR); + auto factor = (**PSCROLLFACTOR <= 0.f || e->source != WLR_AXIS_SOURCE_FINGER ? 1.f : **PSCROLLFACTOR); const auto EMAP = std::unordered_map{{"event", e}}; EMIT_HOOK_EVENT_CANCELLABLE("mouseAxis", EMAP); @@ -689,13 +709,14 @@ void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) { if (!m_bLastFocusOnLS) { const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - const auto PWINDOW = g_pCompositor->vectorToWindowIdeal(MOUSECOORDS); + const auto PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (PWINDOW && PWINDOW->checkInputOnDecos(INPUT_TYPE_AXIS, MOUSECOORDS, e)) return; } - wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source); + wlr_seat_pointer_notify_axis(g_pCompositor->m_sSeat.seat, e->time_msec, e->orientation, factor * e->delta, std::round(factor * e->delta_discrete), e->source, + WLR_AXIS_RELATIVE_DIRECTION_IDENTICAL); } Vector2D CInputManager::getMouseCoordsInternal() { @@ -802,7 +823,8 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { const auto REPEATRATE = g_pConfigManager->getDeviceInt(devname, "repeat_rate", "input:repeat_rate"); const auto REPEATDELAY = g_pConfigManager->getDeviceInt(devname, "repeat_delay", "input:repeat_delay"); - const auto NUMLOCKON = g_pConfigManager->getDeviceInt(devname, "numlock_by_default", "input:numlock_by_default"); + const auto NUMLOCKON = g_pConfigManager->getDeviceInt(devname, "numlock_by_default", "input:numlock_by_default"); + const auto RESOLVEBINDSBYSYM = g_pConfigManager->getDeviceInt(devname, "resolve_binds_by_sym", "input:resolve_binds_by_sym"); const auto FILEPATH = g_pConfigManager->getDeviceString(devname, "kb_file", "input:kb_file"); const auto RULES = g_pConfigManager->getDeviceString(devname, "kb_rules", "input:kb_rules"); @@ -813,7 +835,8 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { const auto ENABLED = HASCONFIG ? g_pConfigManager->getDeviceInt(devname, "enabled") : true; - pKeyboard->enabled = ENABLED; + pKeyboard->enabled = ENABLED; + pKeyboard->resolveBindsBySym = RESOLVEBINDSBYSYM; try { if (NUMLOCKON == pKeyboard->numlockOn && REPEATDELAY == pKeyboard->repeatDelay && REPEATRATE == pKeyboard->repeatRate && RULES != "" && @@ -885,6 +908,9 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } + xkb_state_unref(pKeyboard->xkbTranslationState); + pKeyboard->xkbTranslationState = xkb_state_new(KEYMAP); + wlr_keyboard_set_keymap(wlr_keyboard_from_input_device(pKeyboard->keyboard), KEYMAP); wlr_keyboard_modifiers wlrMods = {0}; @@ -1106,6 +1132,8 @@ void CInputManager::destroyKeyboard(SKeyboard* pKeyboard) { pKeyboard->hyprListener_keyboardMod.removeCallback(); pKeyboard->hyprListener_keyboardKey.removeCallback(); + xkb_state_unref(pKeyboard->xkbTranslationState); + if (pKeyboard->active) { m_lKeyboards.remove(*pKeyboard); @@ -1161,8 +1189,8 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar const auto EMAP = std::unordered_map{{"keyboard", pKeyboard}, {"event", e}}; EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP); - static auto* const PDPMS = &g_pConfigManager->getConfigValuePtr("misc:key_press_enables_dpms")->intValue; - if (*PDPMS && !g_pCompositor->m_bDPMSStateON) { + static auto* const PDPMS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:key_press_enables_dpms"); + if (**PDPMS && !g_pCompositor->m_bDPMSStateON) { // enable dpms g_pKeybindManager->dpms("on"); } @@ -1185,6 +1213,9 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar updateKeyboardsLeds(pKeyboard->keyboard); } + + if (m_bExitTriggered) + g_pCompositor->cleanup(); } void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) { @@ -1457,20 +1488,37 @@ void CInputManager::newTouchDevice(wlr_input_device* pDevice) { } void CInputManager::setTouchDeviceConfigs(STouchDevice* dev) { - auto setConfig = [&](STouchDevice* const PTOUCHDEV) -> void { if (wlr_input_device_is_libinput(PTOUCHDEV->pWlrDevice)) { const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(PTOUCHDEV->pWlrDevice); - const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "transform", "input:touchdevice:transform"), 0, 7); + const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "enabled", "input:touchdevice:enabled"); + const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; + if (libinput_device_config_send_events_get_mode(LIBINPUTDEV) != mode) + libinput_device_config_send_events_set_mode(LIBINPUTDEV, mode); + + const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->name, "transform", "input:touchdevice:transform"), 0, 7); + Debug::log(LOG, "Setting calibration matrix for device {}", PTOUCHDEV->name); if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV)) libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]); - const auto OUTPUT = g_pConfigManager->getDeviceString(PTOUCHDEV->name, "output", "input:touchdevice:output"); - if (!OUTPUT.empty() && OUTPUT != STRVAL_EMPTY) - PTOUCHDEV->boundOutput = OUTPUT; - else - PTOUCHDEV->boundOutput = ""; + auto output = g_pConfigManager->getDeviceString(PTOUCHDEV->name, "output", "input:touchdevice:output"); + bool bound = !output.empty() && output != STRVAL_EMPTY; + const bool AUTODETECT = output == "[[Auto]]"; + if (!bound && AUTODETECT) { + const auto DEFAULTOUTPUT = wlr_touch_from_input_device(PTOUCHDEV->pWlrDevice)->output_name; + if (DEFAULTOUTPUT) { + output = DEFAULTOUTPUT; + bound = true; + } + } + PTOUCHDEV->boundOutput = bound ? output : ""; + const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr; + if (PMONITOR) { + Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV->name, PMONITOR->szName); + wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, PTOUCHDEV->pWlrDevice, PMONITOR->output); + } else if (bound) + Debug::log(ERR, "Failed to bind touch device {} to output '{}': monitor not found", PTOUCHDEV->name, output); } }; @@ -1501,9 +1549,12 @@ void CInputManager::setTabletConfigs() { const auto OUTPUT = g_pConfigManager->getDeviceString(t.name, "output", "input:tablet:output"); const auto PMONITOR = g_pCompositor->getMonitorFromString(OUTPUT); if (!OUTPUT.empty() && OUTPUT != STRVAL_EMPTY && PMONITOR) { + Debug::log(LOG, "Binding tablet {} to output {}", t.name, PMONITOR->szName); wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, t.wlrDevice, PMONITOR->output); wlr_cursor_map_input_to_region(g_pCompositor->m_sWLRCursor, t.wlrDevice, nullptr); - } + t.boundOutput = OUTPUT; + } else if (!PMONITOR) + Debug::log(ERR, "Failed to bind tablet {} to output '{}': monitor not found", t.name, OUTPUT); const auto REGION_POS = g_pConfigManager->getDeviceVec(t.name, "region_position", "input:tablet:region_position"); const auto REGION_SIZE = g_pConfigManager->getDeviceVec(t.name, "region_size", "input:tablet:region_size"); @@ -1647,17 +1698,17 @@ void CInputManager::setCursorIconOnBorder(CWindow* w) { return; } - static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue; - static const auto* PEXTENDBORDERGRAB = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue; + static auto* const PEXTENDBORDERGRAB = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area"); const int BORDERSIZE = w->getRealBorderSize(); + const int ROUNDING = w->rounding(); // give a small leeway (10 px) for corner icon - const auto CORNER = *PROUNDING + BORDERSIZE + 10; + const auto CORNER = ROUNDING + BORDERSIZE + 10; const auto mouseCoords = getMouseCoordsInternal(); CBox box = w->getWindowMainSurfaceBox(); eBorderIconDirection direction = BORDERICON_NONE; - CBox boxFullGrabInput = {box.x - *PEXTENDBORDERGRAB - BORDERSIZE, box.y - *PEXTENDBORDERGRAB - BORDERSIZE, box.width + 2 * (*PEXTENDBORDERGRAB + BORDERSIZE), - box.height + 2 * (*PEXTENDBORDERGRAB + BORDERSIZE)}; + CBox boxFullGrabInput = {box.x - **PEXTENDBORDERGRAB - BORDERSIZE, box.y - **PEXTENDBORDERGRAB - BORDERSIZE, box.width + 2 * (**PEXTENDBORDERGRAB + BORDERSIZE), + box.height + 2 * (**PEXTENDBORDERGRAB + BORDERSIZE)}; if (w->hasPopupAt(mouseCoords)) direction = BORDERICON_NONE; diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 5fa5824e..e32e27f8 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -142,25 +142,28 @@ class CInputManager { // Switches std::list m_lSwitches; - void newTabletTool(wlr_input_device*); - void newTabletPad(wlr_input_device*); - void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false); - void newIdleInhibitor(wlr_idle_inhibitor_v1*); - void recheckIdleInhibitorStatus(); + // Exclusive layer surfaces + std::deque m_dExclusiveLSes; - void onSwipeBegin(wlr_pointer_swipe_begin_event*); - void onSwipeEnd(wlr_pointer_swipe_end_event*); - void onSwipeUpdate(wlr_pointer_swipe_update_event*); + void newTabletTool(wlr_input_device*); + void newTabletPad(wlr_input_device*); + void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false); + void newIdleInhibitor(wlr_idle_inhibitor_v1*); + void recheckIdleInhibitorStatus(); - SSwipeGesture m_sActiveSwipe; + void onSwipeBegin(wlr_pointer_swipe_begin_event*); + void onSwipeEnd(wlr_pointer_swipe_end_event*); + void onSwipeUpdate(wlr_pointer_swipe_update_event*); - SKeyboard* m_pActiveKeyboard = nullptr; + SSwipeGesture m_sActiveSwipe; - CTimer m_tmrLastCursorMovement; + SKeyboard* m_pActiveKeyboard = nullptr; - CInputMethodRelay m_sIMERelay; + CTimer m_tmrLastCursorMovement; - void updateKeyboardsLeds(wlr_input_device* pKeyboard); + CInputMethodRelay m_sIMERelay; + + void updateKeyboardsLeds(wlr_input_device* pKeyboard); // for shared mods uint32_t accumulateModsFromAllKBs(); @@ -243,6 +246,8 @@ class CInputManager { void restoreCursorIconToApp(); // no-op if restored + bool m_bExitTriggered = false; // for exit dispatcher + friend class CKeybindManager; friend class CWLSurface; }; diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index 5b6df08d..aef9bb69 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -122,10 +122,6 @@ void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) { Debug::log(LOG, "IME TextInput Keyboard Grab destroy"); - if (m_pKeyboardGrab->pKeyboard) { - wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &m_pKeyboardGrab->pKeyboard->modifiers); - } - m_pKeyboardGrab.reset(nullptr); }, m_pKeyboardGrab.get(), "IME Keyboard Grab"); diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp index f345e4f5..1cc1b031 100644 --- a/src/managers/input/Swipe.cpp +++ b/src/managers/input/Swipe.cpp @@ -2,11 +2,11 @@ #include "../../Compositor.hpp" void CInputManager::onSwipeBegin(wlr_pointer_swipe_begin_event* e) { - static auto* const PSWIPE = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe")->intValue; - static auto* const PSWIPEFINGERS = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_fingers")->intValue; - static auto* const PSWIPENEW = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new")->intValue; + static auto* const PSWIPE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe"); + static auto* const PSWIPEFINGERS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_fingers"); + static auto* const PSWIPENEW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new"); - if (e->fingers != *PSWIPEFINGERS || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked()) + if (e->fingers != **PSWIPEFINGERS || **PSWIPE == 0 || g_pSessionLockManager->isSessionLocked()) return; int onMonitor = 0; @@ -16,7 +16,7 @@ void CInputManager::onSwipeBegin(wlr_pointer_swipe_begin_event* e) { } } - if (onMonitor < 2 && !*PSWIPENEW) + if (onMonitor < 2 && !**PSWIPENEW) return; // disallow swiping when there's 1 workspace on a monitor beginWorkspaceSwipe(); @@ -44,20 +44,20 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) { if (!m_sActiveSwipe.pWorkspaceBegin) return; // no valid swipe - static auto* const PSWIPEPERC = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_cancel_ratio")->floatValue; - static auto* const PSWIPEDIST = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_distance")->intValue; - static auto* const PSWIPEFORC = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_min_speed_to_force")->intValue; - static auto* const PSWIPENEW = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new")->intValue; - static auto* const PSWIPENUMBER = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_numbered")->intValue; - static auto* const PSWIPEUSER = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_use_r")->intValue; - static auto* const PWORKSPACEGAP = &g_pConfigManager->getConfigValuePtr("general:gaps_workspaces")->intValue; + static auto* const PSWIPEPERC = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_cancel_ratio"); + static auto* const PSWIPEDIST = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_distance"); + static auto* const PSWIPEFORC = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_min_speed_to_force"); + static auto* const PSWIPENEW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new"); + static auto* const PSWIPENUMBER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_numbered"); + static auto* const PSWIPEUSER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_use_r"); + static auto* const PWORKSPACEGAP = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:gaps_workspaces"); const bool VERTANIMS = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle == "slidevert" || m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle.starts_with("slidefadevert"); // commit std::string wsname = ""; - auto workspaceIDLeft = getWorkspaceIDFromString(*PSWIPENUMBER ? "-1" : (*PSWIPEUSER ? "r-1" : "m-1"), wsname); - auto workspaceIDRight = getWorkspaceIDFromString(*PSWIPENUMBER ? "+1" : (*PSWIPEUSER ? "r+1" : "m+1"), wsname); + auto workspaceIDLeft = getWorkspaceIDFromString(**PSWIPENUMBER ? "-1" : (**PSWIPEUSER ? "r-1" : "m-1"), wsname); + auto workspaceIDRight = getWorkspaceIDFromString(**PSWIPENUMBER ? "+1" : (**PSWIPEUSER ? "r+1" : "m+1"), wsname); // If we've been swiping off the right end with PSWIPENEW enabled, there is // no workspace there yet, and we need to choose an ID for a new one now. @@ -68,7 +68,7 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) { // be counterintuitive to swipe rightwards onto a new workspace and end up // left of where we started. Instead, it's one more than the greatest // workspace ID that currently exists. - if (workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID && *PSWIPENEW) { + if (workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID && **PSWIPENEW) { int maxWorkspace = 0; for (const auto& ws : g_pCompositor->m_vWorkspaces) { maxWorkspace = std::max(maxWorkspace, ws->m_iID); @@ -80,12 +80,12 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) { auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER const auto RENDEROFFSETMIDDLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.vec(); - const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + *PWORKSPACEGAP; - const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + *PWORKSPACEGAP; + const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + **PWORKSPACEGAP; + const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + **PWORKSPACEGAP; CWorkspace* pSwitchedTo = nullptr; - if ((abs(m_sActiveSwipe.delta) < *PSWIPEDIST * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_sActiveSwipe.avgSpeed < *PSWIPEFORC))) || + if ((abs(m_sActiveSwipe.delta) < **PSWIPEDIST * **PSWIPEPERC && (**PSWIPEFORC == 0 || (**PSWIPEFORC != 0 && m_sActiveSwipe.avgSpeed < **PSWIPEFORC))) || abs(m_sActiveSwipe.delta) < 2) { // revert if (abs(m_sActiveSwipe.delta) < 2) { @@ -100,16 +100,16 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) { if (PWORKSPACEL) { if (VERTANIMS) - PWORKSPACEL->m_vRenderOffset = Vector2D({0, -YDISTANCE}); + PWORKSPACEL->m_vRenderOffset = Vector2D{0, -YDISTANCE}; else - PWORKSPACEL->m_vRenderOffset = Vector2D({-XDISTANCE, 0}); + PWORKSPACEL->m_vRenderOffset = Vector2D{-XDISTANCE, 0}; } } else if (PWORKSPACER) { // to right if (VERTANIMS) - PWORKSPACER->m_vRenderOffset = Vector2D({0, YDISTANCE}); + PWORKSPACER->m_vRenderOffset = Vector2D{0, YDISTANCE}; else - PWORKSPACER->m_vRenderOffset = Vector2D({XDISTANCE, 0}); + PWORKSPACER->m_vRenderOffset = Vector2D{XDISTANCE, 0}; } m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(); @@ -194,40 +194,40 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { if (!m_sActiveSwipe.pWorkspaceBegin) return; - static auto* const PSWIPEDIST = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_distance")->intValue; - static auto* const PSWIPEINVR = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_invert")->intValue; - static auto* const PSWIPENEW = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new")->intValue; - static auto* const PSWIPEDIRLOCK = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_direction_lock")->intValue; - static auto* const PSWIPEDIRLOCKTHRESHOLD = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_direction_lock_threshold")->intValue; - static auto* const PSWIPEFOREVER = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_forever")->intValue; - static auto* const PSWIPENUMBER = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_numbered")->intValue; - static auto* const PSWIPEUSER = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_use_r")->intValue; - static auto* const PWORKSPACEGAP = &g_pConfigManager->getConfigValuePtr("general:gaps_workspaces")->intValue; + static auto* const PSWIPEDIST = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_distance"); + static auto* const PSWIPEINVR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_invert"); + static auto* const PSWIPENEW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new"); + static auto* const PSWIPEDIRLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_direction_lock"); + static auto* const PSWIPEDIRLOCKTHRESHOLD = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_direction_lock_threshold"); + static auto* const PSWIPEFOREVER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_forever"); + static auto* const PSWIPENUMBER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_numbered"); + static auto* const PSWIPEUSER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_use_r"); + static auto* const PWORKSPACEGAP = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:gaps_workspaces"); - const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + *PWORKSPACEGAP; - const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + *PWORKSPACEGAP; + const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + **PWORKSPACEGAP; + const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + **PWORKSPACEGAP; const bool VERTANIMS = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle == "slidevert" || m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle.starts_with("slidefadevert"); - m_sActiveSwipe.delta += VERTANIMS ? (*PSWIPEINVR ? -e->dy : e->dy) : (*PSWIPEINVR ? -e->dx : e->dx); + m_sActiveSwipe.delta += VERTANIMS ? (**PSWIPEINVR ? -e->dy : e->dy) : (**PSWIPEINVR ? -e->dx : e->dx); m_sActiveSwipe.avgSpeed = (m_sActiveSwipe.avgSpeed * m_sActiveSwipe.speedPoints + abs(e->dx)) / (m_sActiveSwipe.speedPoints + 1); m_sActiveSwipe.speedPoints++; std::string wsname = ""; - auto workspaceIDLeft = getWorkspaceIDFromString(*PSWIPENUMBER ? "-1" : (*PSWIPEUSER ? "r-1" : "m-1"), wsname); - auto workspaceIDRight = getWorkspaceIDFromString(*PSWIPENUMBER ? "+1" : (*PSWIPEUSER ? "r+1" : "m+1"), wsname); + auto workspaceIDLeft = getWorkspaceIDFromString(**PSWIPENUMBER ? "-1" : (**PSWIPEUSER ? "r-1" : "m-1"), wsname); + auto workspaceIDRight = getWorkspaceIDFromString(**PSWIPENUMBER ? "+1" : (**PSWIPEUSER ? "r+1" : "m+1"), wsname); - if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_sActiveSwipe.pWorkspaceBegin->m_iID) && !*PSWIPENEW) { + if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_sActiveSwipe.pWorkspaceBegin->m_iID) && !**PSWIPENEW) { m_sActiveSwipe.pWorkspaceBegin = nullptr; // invalidate the swipe return; } m_sActiveSwipe.pWorkspaceBegin->m_bForceRendering = true; - m_sActiveSwipe.delta = std::clamp(m_sActiveSwipe.delta, (double)-*PSWIPEDIST, (double)*PSWIPEDIST); + m_sActiveSwipe.delta = std::clamp(m_sActiveSwipe.delta, (double)-**PSWIPEDIST, (double)**PSWIPEDIST); - if ((m_sActiveSwipe.pWorkspaceBegin->m_iID == workspaceIDLeft && *PSWIPENEW && (m_sActiveSwipe.delta < 0)) || + if ((m_sActiveSwipe.pWorkspaceBegin->m_iID == workspaceIDLeft && **PSWIPENEW && (m_sActiveSwipe.delta < 0)) || (m_sActiveSwipe.delta > 0 && g_pCompositor->getWindowsOnWorkspace(m_sActiveSwipe.pWorkspaceBegin->m_iID) == 0 && workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID) || (m_sActiveSwipe.delta < 0 && m_sActiveSwipe.pWorkspaceBegin->m_iID <= workspaceIDLeft)) { @@ -236,10 +236,10 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { return; } - if (*PSWIPEDIRLOCK) { + if (**PSWIPEDIRLOCK) { if (m_sActiveSwipe.initialDirection != 0 && m_sActiveSwipe.initialDirection != (m_sActiveSwipe.delta < 0 ? -1 : 1)) m_sActiveSwipe.delta = 0; - else if (m_sActiveSwipe.initialDirection == 0 && abs(m_sActiveSwipe.delta) > *PSWIPEDIRLOCKTHRESHOLD) + else if (m_sActiveSwipe.initialDirection == 0 && abs(m_sActiveSwipe.delta) > **PSWIPEDIRLOCKTHRESHOLD) m_sActiveSwipe.initialDirection = m_sActiveSwipe.delta < 0 ? -1 : 1; } @@ -247,13 +247,13 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft); if (workspaceIDLeft > m_sActiveSwipe.pWorkspaceBegin->m_iID || !PWORKSPACE) { - if (*PSWIPENEW || *PSWIPENUMBER) { + if (**PSWIPENEW || **PSWIPENUMBER) { g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor); if (VERTANIMS) - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE)); else - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE, 0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE, 0)); g_pCompositor->updateWorkspaceWindowDecos(m_sActiveSwipe.pWorkspaceBegin->m_iID); return; @@ -265,7 +265,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); - if (workspaceIDLeft != workspaceIDRight) { + if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_sActiveSwipe.pWorkspaceBegin->m_iID) { const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); if (PWORKSPACER) { @@ -275,11 +275,11 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { } if (VERTANIMS) { - PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE - YDISTANCE)); - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE)); + PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE - YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE)); } else { - PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE - XDISTANCE, 0)); - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE, 0)); + PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE - XDISTANCE, 0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE, 0)); } g_pCompositor->updateWorkspaceWindowDecos(workspaceIDLeft); @@ -287,13 +287,13 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight); if (workspaceIDRight < m_sActiveSwipe.pWorkspaceBegin->m_iID || !PWORKSPACE) { - if (*PSWIPENEW || *PSWIPENUMBER) { + if (**PSWIPENEW || **PSWIPENUMBER) { g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor); if (VERTANIMS) - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE)); else - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE, 0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE, 0)); g_pCompositor->updateWorkspaceWindowDecos(m_sActiveSwipe.pWorkspaceBegin->m_iID); return; @@ -305,7 +305,7 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { PWORKSPACE->m_bForceRendering = true; PWORKSPACE->m_fAlpha.setValueAndWarp(1.f); - if (workspaceIDLeft != workspaceIDRight) { + if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_sActiveSwipe.pWorkspaceBegin->m_iID) { const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); if (PWORKSPACEL) { @@ -315,11 +315,11 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { } if (VERTANIMS) { - PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE + YDISTANCE)); - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / *PSWIPEDIST) * YDISTANCE)); + PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE + YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(0, ((-m_sActiveSwipe.delta) / **PSWIPEDIST) * YDISTANCE)); } else { - PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE + XDISTANCE, 0)); - m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / *PSWIPEDIST) * XDISTANCE, 0)); + PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE + XDISTANCE, 0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / **PSWIPEDIST) * XDISTANCE, 0)); } g_pCompositor->updateWorkspaceWindowDecos(workspaceIDRight); @@ -329,8 +329,8 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) { g_pCompositor->updateWorkspaceWindowDecos(m_sActiveSwipe.pWorkspaceBegin->m_iID); - if (*PSWIPEFOREVER) { - if (abs(m_sActiveSwipe.delta) >= *PSWIPEDIST) { + if (**PSWIPEFOREVER) { + if (abs(m_sActiveSwipe.delta) >= **PSWIPEDIST) { onSwipeEnd(nullptr); beginWorkspaceSwipe(); } diff --git a/src/meson.build b/src/meson.build index 0af864b9..2d72280c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,6 +11,7 @@ executable('Hyprland', src, dependency('wayland-client'), wlroots.get_variable('wlroots'), dependency('cairo'), + dependency('hyprlang', version: '>= 0.3.2'), dependency('libdrm'), dependency('egl'), dependency('xkbcommon'), diff --git a/src/plugins/HookSystem.cpp b/src/plugins/HookSystem.cpp index e52fd749..ca38fe95 100644 --- a/src/plugins/HookSystem.cpp +++ b/src/plugins/HookSystem.cpp @@ -74,9 +74,10 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr std::string code = probe.assembly.substr(lastAsmNewline, probe.assembly.find("\n", lastAsmNewline) - lastAsmNewline); if (code.contains("%rip")) { CVarList tokens{code, 0, 's'}; - size_t plusPresent = tokens[1][0] == '+' ? 1 : 0; - std::string addr = tokens[1].substr(plusPresent, tokens[1].find("(%rip)") - plusPresent); - const uint64_t OFFSET = configStringToInt(addr); + size_t plusPresent = tokens[1][0] == '+' ? 1 : 0; + size_t minusPresent = tokens[1][0] == '-' ? 1 : 0; + std::string addr = tokens[1].substr((plusPresent || minusPresent), tokens[1].find("(%rip)") - (plusPresent || minusPresent)); + const uint64_t OFFSET = (minusPresent ? -1 : 1) * configStringToInt(addr); if (OFFSET == 0) return {}; const uint64_t DESTINATION = currentAddress + OFFSET + len; @@ -190,9 +191,9 @@ bool CFunctionHook::hook() { (uint64_t)((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS)); // make jump to hk - const auto PAGESIZE = sysconf(_SC_PAGE_SIZE); - const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE); - const size_t PROTLEN = std::ceil((float)(ORIGSIZE + ((uint64_t)m_pSource - (uint64_t)PROTSTART)) / (float)PAGESIZE) * PAGESIZE; + const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE); + const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE_VAR); + const size_t PROTLEN = std::ceil((float)(ORIGSIZE + ((uint64_t)m_pSource - (uint64_t)PROTSTART)) / (float)PAGESIZE_VAR) * PAGESIZE_VAR; mprotect((uint8_t*)PROTSTART, PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC); memcpy((uint8_t*)m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); diff --git a/src/plugins/PluginAPI.cpp b/src/plugins/PluginAPI.cpp index 283472fa..b6326960 100644 --- a/src/plugins/PluginAPI.cpp +++ b/src/plugins/PluginAPI.cpp @@ -50,9 +50,9 @@ APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, HOOK_CALLBACK_FN* fn APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, const std::string& args, const std::string& format) { if (args.empty()) - return HyprCtl::makeDynamicCall(format + "/" + call); + return g_pHyprCtl->makeDynamicCall(format + "/" + call); else - return HyprCtl::makeDynamicCall(format + "/" + call + " " + args); + return g_pHyprCtl->makeDynamicCall(format + "/" + call + " " + args); } APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) { @@ -147,7 +147,7 @@ APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecor return false; } -APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value) { +APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); if (!g_pPluginSystem->m_bAllowConfigVars) @@ -163,7 +163,7 @@ APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, return true; } -APICALL bool HyprlandAPI::addConfigKeyword(HANDLE handle, const std::string& name, std::function fn) { +APICALL bool HyprlandAPI::addConfigKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); if (!g_pPluginSystem->m_bAllowConfigVars) @@ -172,17 +172,20 @@ APICALL bool HyprlandAPI::addConfigKeyword(HANDLE handle, const std::string& nam if (!PLUGIN) return false; - g_pConfigManager->addPluginKeyword(handle, name, fn); + g_pConfigManager->addPluginKeyword(handle, name, fn, opts); return true; } -APICALL SConfigValue* HyprlandAPI::getConfigValue(HANDLE handle, const std::string& name) { +APICALL Hyprlang::CConfigValue* HyprlandAPI::getConfigValue(HANDLE handle, const std::string& name) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); if (!PLUGIN) return nullptr; - return g_pConfigManager->getConfigValuePtrSafe(name); + if (name.starts_with("plugin:")) + return g_pConfigManager->getHyprlangConfigValuePtr(name.substr(7), "plugin"); + + return g_pConfigManager->getHyprlangConfigValuePtr(name); } APICALL void* HyprlandAPI::getFunctionAddressFromSignature(HANDLE handle, const std::string& sig) { @@ -362,4 +365,28 @@ APICALL SVersionInfo HyprlandAPI::getHyprlandVersion(HANDLE handle) { return {}; return {GIT_COMMIT_HASH, GIT_TAG, GIT_DIRTY != std::string(""), GIT_BRANCH, GIT_COMMIT_MESSAGE}; +} + +APICALL std::shared_ptr HyprlandAPI::registerHyprCtlCommand(HANDLE handle, SHyprCtlCommand cmd) { + auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); + + if (!PLUGIN) + return nullptr; + + auto PTR = g_pHyprCtl->registerCommand(cmd); + PLUGIN->registeredHyprctlCommands.push_back(PTR); + return PTR; +} + +APICALL bool HyprlandAPI::unregisterHyprCtlCommand(HANDLE handle, std::shared_ptr cmd) { + + auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); + + if (!PLUGIN) + return false; + + std::erase(PLUGIN->registeredHyprctlCommands, cmd); + g_pHyprCtl->unregisterCommand(cmd); + + return true; } \ No newline at end of file diff --git a/src/plugins/PluginAPI.hpp b/src/plugins/PluginAPI.hpp index 12e7c128..a7a998b4 100644 --- a/src/plugins/PluginAPI.hpp +++ b/src/plugins/PluginAPI.hpp @@ -112,7 +112,7 @@ namespace HyprlandAPI { returns: true on success, false on fail */ - APICALL bool addConfigValue(HANDLE handle, const std::string& name, const SConfigValue& value); + APICALL bool addConfigValue(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value); /* Add a config keyword. @@ -120,15 +120,17 @@ namespace HyprlandAPI { returns: true on success, false on fail */ - APICALL bool addConfigKeyword(HANDLE handle, const std::string& name, std::function fn); + APICALL bool addConfigKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts); /* Get a config value. + Please see the header or https://hyprland.org/hyprlang/ for docs regarding Hyprlang types. + returns: a pointer to the config value struct, which is guaranteed to be valid for the life of this plugin, unless another `addConfigValue` is called afterwards. nullptr on error. */ - APICALL SConfigValue* getConfigValue(HANDLE handle, const std::string& name); + APICALL Hyprlang::CConfigValue* getConfigValue(HANDLE handle, const std::string& name); /* Register a static (pointer) callback to a selected event. @@ -273,6 +275,20 @@ namespace HyprlandAPI { for a different hash. */ APICALL SVersionInfo getHyprlandVersion(HANDLE handle); + + /* + Registers a hyprctl command + + returns: Pointer. Nullptr on fail. + */ + APICALL std::shared_ptr registerHyprCtlCommand(HANDLE handle, SHyprCtlCommand cmd); + + /* + Unregisters a hyprctl command + + returns: true on success. False otherwise. + */ + APICALL bool unregisterHyprCtlCommand(HANDLE handle, std::shared_ptr cmd); }; /* diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp index 1468cb79..44b4de1f 100644 --- a/src/plugins/PluginSystem.cpp +++ b/src/plugins/PluginSystem.cpp @@ -113,6 +113,10 @@ void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) { for (auto& d : rdi) HyprlandAPI::removeDispatcher(plugin->m_pHandle, d); + const auto rhc = plugin->registeredHyprctlCommands; + for (auto& c : rhc) + HyprlandAPI::unregisterHyprCtlCommand(plugin->m_pHandle, c); + g_pConfigManager->removePluginConfig(plugin->m_pHandle); dlclose(plugin->m_pHandle); diff --git a/src/plugins/PluginSystem.hpp b/src/plugins/PluginSystem.hpp index f0c9c9ad..ce2c2bb2 100644 --- a/src/plugins/PluginSystem.hpp +++ b/src/plugins/PluginSystem.hpp @@ -23,6 +23,7 @@ class CPlugin { std::vector registeredDecorations; std::vector> registeredCallbacks; std::vector registeredDispatchers; + std::vector> registeredHyprctlCommands; }; class CPluginSystem { diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 18fe09f4..6d0aa8bc 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -95,6 +95,9 @@ static SScreencopyFrame* frameFromResource(wl_resource* resource) { } void CScreencopyProtocolManager::removeClient(CScreencopyClient* client, bool force) { + if (!client) + return; + if (!force) { if (!client || client->ref <= 0) return; @@ -104,6 +107,12 @@ void CScreencopyProtocolManager::removeClient(CScreencopyClient* client, bool fo } m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits??? + + for (auto& f : m_lFrames) { + // avoid dangling ptrs + if (f.client == client) + f.client = nullptr; + } } static void handleManagerResourceDestroy(wl_resource* resource) { @@ -211,6 +220,18 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r PCLIENT->ref++; g_pHyprRenderer->makeEGLCurrent(); + + if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(PFRAME->pMonitor)) { + const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(PFRAME->pMonitor); + // bind the fb for its format. Suppress gl errors. +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, RDATA.offloadFB.m_iFb); +#else + glBindFramebuffer(GL_FRAMEBUFFER, RDATA.offloadFB.m_iFb); +#endif + } else + Debug::log(ERR, "No RDATA in screencopy???"); + PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PFRAME->pMonitor); if (PFRAME->shmFormat == DRM_FORMAT_INVALID) { Debug::log(ERR, "No format supported by renderer in capture output"); @@ -263,6 +284,13 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou return; } + if (!g_pCompositor->monitorExists(PFRAME->pMonitor)) { + Debug::log(ERR, "client requested sharing of a monitor that is gone"); + zwlr_screencopy_frame_v1_send_failed(PFRAME->resource); + removeFrame(PFRAME); + return; + } + const auto PBUFFER = wlr_buffer_try_from_resource(buffer); if (!PBUFFER) { Debug::log(ERR, "[sc] invalid buffer in {:x}", (uintptr_t)PFRAME); @@ -328,7 +356,7 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou g_pHyprRenderer->m_bSoftwareCursorsLocked = true; if (!PFRAME->withDamage) - g_pCompositor->scheduleFrameForMonitor(PFRAME->pMonitor); + g_pHyprRenderer->damageMonitor(PFRAME->pMonitor); } void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e) { @@ -412,16 +440,21 @@ void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) { if (!frame->withDamage) return; - for (auto& RECT : frame->pMonitor->lastFrameDamage.getRects()) { + // TODO: + // add a damage ring for this. - if (frame->buffer->width < 1 || frame->buffer->height < 1 || frame->buffer->width - RECT.x1 < 1 || frame->buffer->height - RECT.y1 < 1) { - Debug::log(ERR, "[sc] Failed to send damage"); - break; - } + // for (auto& RECT : frame->pMonitor->lastFrameDamage.getRects()) { - zwlr_screencopy_frame_v1_send_damage(frame->resource, std::clamp(RECT.x1, 0, frame->buffer->width), std::clamp(RECT.y1, 0, frame->buffer->height), - std::clamp(RECT.x2 - RECT.x1, 0, frame->buffer->width - RECT.x1), std::clamp(RECT.y2 - RECT.y1, 0, frame->buffer->height - RECT.y1)); - } + // if (frame->buffer->width < 1 || frame->buffer->height < 1 || frame->buffer->width - RECT.x1 < 1 || frame->buffer->height - RECT.y1 < 1) { + // Debug::log(ERR, "[sc] Failed to send damage"); + // break; + // } + + // zwlr_screencopy_frame_v1_send_damage(frame->resource, std::clamp(RECT.x1, 0, frame->buffer->width), std::clamp(RECT.y1, 0, frame->buffer->height), + // std::clamp(RECT.x2 - RECT.x1, 0, frame->buffer->width - RECT.x1), std::clamp(RECT.y2 - RECT.y1, 0, frame->buffer->height - RECT.y1)); + // } + + zwlr_screencopy_frame_v1_send_damage(frame->resource, 0, 0, frame->buffer->width, frame->buffer->height); } bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) { diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index 425572eb..e7ea787d 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -178,6 +178,18 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou const auto PMONITOR = g_pCompositor->getMonitorFromID(PFRAME->pWindow->m_iMonitorID); g_pHyprRenderer->makeEGLCurrent(); + + if (g_pHyprOpenGL->m_mMonitorRenderResources.contains(PMONITOR)) { + const auto& RDATA = g_pHyprOpenGL->m_mMonitorRenderResources.at(PMONITOR); + // bind the fb for its format. Suppress gl errors. +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, RDATA.offloadFB.m_iFb); +#else + glBindFramebuffer(GL_FRAMEBUFFER, RDATA.offloadFB.m_iFb); +#endif + } else + Debug::log(ERR, "No RDATA in toplevelexport???"); + PFRAME->shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR); if (PFRAME->shmFormat == DRM_FORMAT_INVALID) { Debug::log(ERR, "No format supported by renderer in capture toplevel"); @@ -224,6 +236,13 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r return; } + if (!g_pCompositor->windowValidMapped(PFRAME->pWindow)) { + Debug::log(ERR, "Client requested sharing of window handle {:x} which is gone!", (uintptr_t)PFRAME->pWindow); + hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource); + removeFrame(PFRAME); + return; + } + if (!PFRAME->pWindow->m_bIsMapped || PFRAME->pWindow->isHidden()) { Debug::log(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", PFRAME->pWindow); hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource); diff --git a/src/protocols/XDGOutput.cpp b/src/protocols/XDGOutput.cpp index 49845b94..9f6e071d 100644 --- a/src/protocols/XDGOutput.cpp +++ b/src/protocols/XDGOutput.cpp @@ -112,7 +112,7 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(wl_client* client, wl_resource* r } void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) { - static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); if (!pOutput->resource->good() || !pOutput->monitor) return; @@ -120,7 +120,7 @@ void CXDGOutputProtocol::updateOutputDetails(SXDGOutput* pOutput) { const auto POS = pOutput->isXWayland ? pOutput->monitor->vecXWaylandPosition : pOutput->monitor->vecPosition; zxdg_output_v1_send_logical_position(pOutput->resource->resource(), POS.x, POS.y); - if (*PXWLFORCESCALEZERO && pOutput->isXWayland) + if (**PXWLFORCESCALEZERO && pOutput->isXWayland) zxdg_output_v1_send_logical_size(pOutput->resource->resource(), pOutput->monitor->vecTransformedSize.x, pOutput->monitor->vecTransformedSize.y); else zxdg_output_v1_send_logical_size(pOutput->resource->resource(), pOutput->monitor->vecSize.x, pOutput->monitor->vecSize.y); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index cb468932..bab07528 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -120,18 +120,18 @@ GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { // passes requiring introspection are the ones that need to render blur. - static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; - static auto* const PXRAY = &g_pConfigManager->getConfigValuePtr("decoration:blur:xray")->intValue; - static auto* const POPTIM = &g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations")->intValue; - static auto* const PBLURSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:blur:special")->intValue; + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + static auto* const PXRAY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:xray"); + static auto* const POPTIM = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations"); + static auto* const PBLURSPECIAL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:special"); - if (m_RenderData.mouseZoomFactor != 1.0) + if (m_RenderData.mouseZoomFactor != 1.0 || g_pHyprRenderer->m_bCrashingInProgress) return true; if (!pMonitor->mirrors.empty()) return true; - if (*PBLUR == 0) + if (**PBLUR == 0) return false; if (m_RenderData.pCurrentMonData->blurFBShouldRender) @@ -141,17 +141,28 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { return false; for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { - const auto XRAYMODE = ls->xray == -1 ? *PXRAY : ls->xray; + const auto XRAYMODE = ls->xray == -1 ? **PXRAY : ls->xray; if (ls->forceBlur && !XRAYMODE) return true; } for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - const auto XRAYMODE = ls->xray == -1 ? *PXRAY : ls->xray; + const auto XRAYMODE = ls->xray == -1 ? **PXRAY : ls->xray; if (ls->forceBlur && !XRAYMODE) return true; } + // these two block optimization + for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { + if (ls->forceBlur) + return true; + } + + for (auto& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { + if (ls->forceBlur) + return true; + } + if (*PBLURSPECIAL) { for (auto& ws : g_pCompositor->m_vWorkspaces) { if (!ws->m_bIsSpecialWorkspace || ws->m_iMonitorID != pMonitor->ID) @@ -164,11 +175,11 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { } } - if (*PXRAY) + if (**PXRAY) return false; for (auto& w : g_pCompositor->m_vWindows) { - if (!w->m_bIsMapped || w->isHidden() || (!w->m_bIsFloating && *POPTIM && !g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID))) + if (!w->m_bIsMapped || w->isHidden() || (!w->m_bIsFloating && **POPTIM && !g_pCompositor->isWorkspaceSpecial(w->m_iWorkspaceID))) continue; if (!g_pHyprRenderer->shouldRenderWindow(w.get())) @@ -186,19 +197,44 @@ bool CHyprOpenGLImpl::passRequiresIntrospection(CMonitor* pMonitor) { return false; } -void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* fb) { +void CHyprOpenGLImpl::begin(CMonitor* pMonitor, const CRegion& damage_, CFramebuffer* fb, std::optional finalDamage) { m_RenderData.pMonitor = pMonitor; + static auto* const PFORCEINTROSPECTION = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("opengl:force_introspection"); + +#ifndef GLES2 + + const GLenum RESETSTATUS = glGetGraphicsResetStatus(); + if (RESETSTATUS != GL_NO_ERROR) { + std::string errStr = ""; + switch (RESETSTATUS) { + case GL_GUILTY_CONTEXT_RESET: errStr = "GL_GUILTY_CONTEXT_RESET"; break; + case GL_INNOCENT_CONTEXT_RESET: errStr = "GL_INNOCENT_CONTEXT_RESET"; break; + case GL_UNKNOWN_CONTEXT_RESET: errStr = "GL_UNKNOWN_CONTEXT_RESET"; break; + default: errStr = "UNKNOWN??"; break; + } + RASSERT(false, "Aborting, glGetGraphicsResetStatus returned {}. Cannot continue until proper GPU reset handling is implemented.", errStr); + return; + } + +#endif + TRACY_GPU_ZONE("RenderBegin"); glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); matrixProjection(m_RenderData.projection, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, WL_OUTPUT_TRANSFORM_NORMAL); + if (m_mMonitorRenderResources.contains(pMonitor) && m_mMonitorRenderResources.at(pMonitor).offloadFB.m_vSize != pMonitor->vecPixelSize) + destroyMonitorResources(pMonitor); + m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor]; + if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) + initShaders(); + // ensure a framebuffer for the monitor exists - if (!m_mMonitorRenderResources.contains(pMonitor) || m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { + if (m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { m_RenderData.pCurrentMonData->stencilTex.allocate(); m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; @@ -210,29 +246,28 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); m_RenderData.pCurrentMonData->offMainFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); - - createBGTextureForMonitor(pMonitor); } if (m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_RenderData.pMonitor->mirrors.empty()) m_RenderData.pCurrentMonData->monitorMirrorFB.release(); - if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) - initShaders(); - - m_RenderData.damage.set(*pDamage); + m_RenderData.damage.set(damage_); + m_RenderData.finalDamage.set(finalDamage.value_or(damage_)); m_bFakeFrame = fb; if (m_bReloadScreenShader) { - m_bReloadScreenShader = false; - applyScreenShader(g_pConfigManager->getString("decoration:screen_shader")); + m_bReloadScreenShader = false; + static auto* const PSHADER = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("decoration:screen_shader"); + applyScreenShader(*PSHADER); } - const auto PRBO = g_pHyprRenderer->getCurrentRBO(); - const bool FBPROPERSIZE = fb && fb->m_vSize == pMonitor->vecPixelSize; + const auto PRBO = g_pHyprRenderer->getCurrentRBO(); + const bool FBPROPERSIZE = !fb || fb->m_vSize == pMonitor->vecPixelSize; + const bool USERFORCEDINTROSPECTION = **PFORCEINTROSPECTION == 1 ? true : (**PFORCEINTROSPECTION == 2 ? g_pHyprRenderer->isNvidia() : false); // 0 - no, 1 - yes, 2 - nvidia only - if (!FBPROPERSIZE || m_sFinalScreenShader.program > 0 || (PRBO && pMonitor->vecPixelSize != PRBO->getFB()->m_vSize) || passRequiresIntrospection(pMonitor)) { + if (USERFORCEDINTROSPECTION || m_RenderData.forceIntrospection || !FBPROPERSIZE || m_sFinalScreenShader.program > 0 || + (PRBO && pMonitor->vecPixelSize != PRBO->getFB()->m_vSize) || passRequiresIntrospection(pMonitor)) { // we have to offload // bind the offload Hypr Framebuffer m_RenderData.pCurrentMonData->offloadFB.bind(); @@ -255,7 +290,7 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, CRegion* pDamage, CFramebuffer* } void CHyprOpenGLImpl::end() { - static auto* const PZOOMRIGID = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_rigid")->intValue; + static auto* const PZOOMRIGID = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_rigid"); TRACY_GPU_ZONE("RenderEnd"); @@ -264,7 +299,7 @@ void CHyprOpenGLImpl::end() { // end the render, copy the data to the WLR framebuffer if (m_bOffloadedFramebuffer) { - m_RenderData.damage = m_RenderData.pMonitor->lastFrameDamage; + m_RenderData.damage = m_RenderData.finalDamage; m_RenderData.outFB->bind(); @@ -275,7 +310,7 @@ void CHyprOpenGLImpl::end() { (g_pInputManager->getMouseCoordsInternal() - m_RenderData.pMonitor->vecPosition) * m_RenderData.pMonitor->scale : m_RenderData.pMonitor->vecTransformedSize / 2.f; - monbox.translate(-ZOOMCENTER).scale(m_RenderData.mouseZoomFactor).translate(*PZOOMRIGID ? m_RenderData.pMonitor->vecTransformedSize / 2.0 : ZOOMCENTER); + monbox.translate(-ZOOMCENTER).scale(m_RenderData.mouseZoomFactor).translate(**PZOOMRIGID ? m_RenderData.pMonitor->vecTransformedSize / 2.0 : ZOOMCENTER); if (monbox.x > 0) monbox.x = 0; @@ -294,7 +329,7 @@ void CHyprOpenGLImpl::end() { blend(false); - if (m_sFinalScreenShader.program < 1) + if (m_sFinalScreenShader.program < 1 && !g_pHyprRenderer->m_bCrashingInProgress) renderTexturePrimitive(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox); else renderTexture(m_RenderData.pCurrentMonData->offloadFB.m_cTex, &monbox, 1.f); @@ -307,9 +342,24 @@ void CHyprOpenGLImpl::end() { } // reset our data - m_RenderData.pMonitor = nullptr; - m_RenderData.mouseZoomFactor = 1.f; - m_RenderData.mouseZoomUseMouse = true; + m_RenderData.pMonitor = nullptr; + m_RenderData.mouseZoomFactor = 1.f; + m_RenderData.mouseZoomUseMouse = true; + m_RenderData.forceIntrospection = false; + m_RenderData.currentFB = nullptr; + m_RenderData.mainFB = nullptr; + m_RenderData.outFB = nullptr; + + // check for gl errors + const GLenum ERR = glGetError(); + + if (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */ + RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented."); +} + +void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional finalDamage) { + m_RenderData.damage.set(damage_); + m_RenderData.finalDamage.set(finalDamage.value_or(damage_)); } void CHyprOpenGLImpl::initShaders() { @@ -476,6 +526,8 @@ void CHyprOpenGLImpl::initShaders() { void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { + static auto* const PDT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:damage_tracking"); + m_sFinalScreenShader.destroy(); if (path == "" || path == STRVAL_EMPTY) @@ -497,11 +549,11 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { return; } - m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj"); - m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex"); - m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time"); - m_sFinalScreenShader.output = glGetUniformLocation(m_sFinalScreenShader.program, "output"); - if (m_sFinalScreenShader.time != -1 && g_pConfigManager->getInt("debug:damage_tracking") != 0 && !g_pHyprRenderer->m_bCrashingInProgress) { + m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj"); + m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex"); + m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time"); + m_sFinalScreenShader.wl_output = glGetUniformLocation(m_sFinalScreenShader.program, "wl_output"); + if (m_sFinalScreenShader.time != -1 && **PDT != 0 && !g_pHyprRenderer->m_bCrashingInProgress) { // The screen shader uses the "time" uniform // Since the screen shader could change every frame, damage tracking *needs* to be disabled g_pConfigManager->addParseError("Screen shader: Screen shader uses uniform 'time', which requires debug:damage_tracking to be switched off.\n" @@ -637,12 +689,12 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CColor& col, CRegion TRACY_GPU_ZONE("RenderRectWithDamage"); CBox newBox = *box; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); box = &newBox; float matrix[9]; - wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0, + wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot, m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here float glMatrix[9]; @@ -719,18 +771,19 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* alpha = std::clamp(alpha, 0.f, 1.f); - if (m_RenderData.damage.empty()) + if (damage->empty()) return; CBox newBox = *pBox; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); - static auto* const PDIMINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue; + static auto* const PDIMINACTIVE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_inactive"); + static auto* const PDT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:damage_tracking"); // get transform const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); float matrix[9]; - wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data()); + wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data()); float glMatrix[9]; wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); @@ -785,15 +838,15 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* #endif glUniform1i(shader->tex, 0); - if ((usingFinalShader && g_pConfigManager->getInt("debug:damage_tracking") == 0) || CRASHING) { + if ((usingFinalShader && **PDT == 0) || CRASHING) { glUniform1f(shader->time, m_tGlobalTimer.getSeconds()); } else if (usingFinalShader && shader->time != -1) { // Don't let time be unitialised glUniform1f(shader->time, 0.f); } - if (usingFinalShader && shader->output != -1) - glUniform1i(shader->output, m_RenderData.pMonitor->ID); + if (usingFinalShader && shader->wl_output != -1) + glUniform1i(shader->wl_output, m_RenderData.pMonitor->ID); if (CRASHING) { glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort); @@ -826,7 +879,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y); glUniform1f(shader->radius, round); - if (allowDim && m_pCurrentWindow && *PDIMINACTIVE) { + if (allowDim && m_pCurrentWindow && **PDIMINACTIVE) { glUniform1i(shader->applyTint, 1); const auto DIM = m_pCurrentWindow->m_fDimPercent.fl(); glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM); @@ -886,12 +939,12 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) { return; CBox newBox = *pBox; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); // get transform const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); float matrix[9]; - wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data()); + wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data()); float glMatrix[9]; wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); @@ -940,12 +993,12 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame return; CBox newBox = *pBox; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); // get transform const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform); float matrix[9]; - wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, 0, m_RenderData.pMonitor->projMatrix.data()); + wlr_matrix_project_box(matrix, newBox.pWlr(), TRANSFORM, newBox.rot, m_RenderData.pMonitor->projMatrix.data()); float glMatrix[9]; wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); @@ -1010,16 +1063,16 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix); // get the config settings - static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:size")->intValue; - static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur:passes")->intValue; - static auto* const PBLURVIBRANCY = &g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy")->floatValue; - static auto* const PBLURVIBRANCYDARKNESS = &g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy_darkness")->floatValue; + static auto* const PBLURSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:size"); + static auto* const PBLURPASSES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:passes"); + static auto* const PBLURVIBRANCY = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy"); + static auto* const PBLURVIBRANCYDARKNESS = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:vibrancy_darkness"); // prep damage CRegion damage{*originalDamage}; wlr_region_transform(damage.pixman(), damage.pixman(), wlr_output_transform_invert(m_RenderData.pMonitor->transform), m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y); - wlr_region_expand(damage.pixman(), damage.pixman(), *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES)); + wlr_region_expand(damage.pixman(), damage.pixman(), **PBLURPASSES > 10 ? pow(2, 15) : std::clamp(**PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, **PBLURPASSES)); // helper const auto PMIRRORFB = &m_RenderData.pCurrentMonData->mirrorFB; @@ -1030,8 +1083,8 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o // Begin with base color adjustments - global brightness and contrast // TODO: make this a part of the first pass maybe to save on a drawcall? { - static auto* const PBLURCONTRAST = &g_pConfigManager->getConfigValuePtr("decoration:blur:contrast")->floatValue; - static auto* const PBLURBRIGHTNESS = &g_pConfigManager->getConfigValuePtr("decoration:blur:brightness")->floatValue; + static auto* const PBLURCONTRAST = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:contrast"); + static auto* const PBLURBRIGHTNESS = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:brightness"); PMIRRORSWAPFB->bind(); @@ -1049,8 +1102,8 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o wlr_matrix_transpose(glMatrix, glMatrix); glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj, 1, GL_FALSE, glMatrix); #endif - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast, *PBLURCONTRAST); - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness, *PBLURBRIGHTNESS); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast, **PBLURCONTRAST); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness, **PBLURBRIGHTNESS); glUniform1i(m_RenderData.pCurrentMonData->m_shBLURPREPARE.tex, 0); glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); @@ -1094,13 +1147,13 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o wlr_matrix_transpose(glMatrix, glMatrix); glUniformMatrix3fv(pShader->proj, 1, GL_FALSE, glMatrix); #endif - glUniform1f(pShader->radius, *PBLURSIZE * a); // this makes the blursize change with a + glUniform1f(pShader->radius, **PBLURSIZE * a); // this makes the blursize change with a if (pShader == &m_RenderData.pCurrentMonData->m_shBLUR1) { glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x / 2.f), 0.5f / (m_RenderData.pMonitor->vecPixelSize.y / 2.f)); - glUniform1i(m_RenderData.pCurrentMonData->m_shBLUR1.passes, *PBLURPASSES); - glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy, *PBLURVIBRANCY); - glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness, *PBLURVIBRANCYDARKNESS); + glUniform1i(m_RenderData.pCurrentMonData->m_shBLUR1.passes, **PBLURPASSES); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy, **PBLURVIBRANCY); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness, **PBLURVIBRANCYDARKNESS); } else glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR2.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x * 2.f), 0.5f / (m_RenderData.pMonitor->vecPixelSize.y * 2.f)); @@ -1137,20 +1190,20 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o CRegion tempDamage{damage}; // and draw - for (int i = 1; i <= *PBLURPASSES; ++i) { + for (int i = 1; i <= **PBLURPASSES; ++i) { wlr_region_scale(tempDamage.pixman(), damage.pixman(), 1.f / (1 << i)); drawPass(&m_RenderData.pCurrentMonData->m_shBLUR1, &tempDamage); // down } - for (int i = *PBLURPASSES - 1; i >= 0; --i) { + for (int i = **PBLURPASSES - 1; i >= 0; --i) { wlr_region_scale(tempDamage.pixman(), damage.pixman(), 1.f / (1 << i)); // when upsampling we make the region twice as big drawPass(&m_RenderData.pCurrentMonData->m_shBLUR2, &tempDamage); // up } // finalize the image { - static auto* const PBLURNOISE = &g_pConfigManager->getConfigValuePtr("decoration:blur:noise")->floatValue; - static auto* const PBLURBRIGHTNESS = &g_pConfigManager->getConfigValuePtr("decoration:blur:brightness")->floatValue; + static auto* const PBLURNOISE = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:noise"); + static auto* const PBLURBRIGHTNESS = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:brightness"); if (currentRenderToFB == PMIRRORFB) PMIRRORSWAPFB->bind(); @@ -1171,8 +1224,8 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o wlr_matrix_transpose(glMatrix, glMatrix); glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_FALSE, glMatrix); #endif - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.noise, *PBLURNOISE); - glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness, *PBLURBRIGHTNESS); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.noise, **PBLURNOISE); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness, **PBLURBRIGHTNESS); glUniform1i(m_RenderData.pCurrentMonData->m_shBLURFINISH.tex, 0); @@ -1211,11 +1264,11 @@ void CHyprOpenGLImpl::markBlurDirtyForMonitor(CMonitor* pMonitor) { } void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) { - static auto* const PBLURNEWOPTIMIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations")->intValue; - static auto* const PBLURXRAY = &g_pConfigManager->getConfigValuePtr("decoration:blur:xray")->intValue; - static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; + static auto* const PBLURNEWOPTIMIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations"); + static auto* const PBLURXRAY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:xray"); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); - if (!*PBLURNEWOPTIMIZE || !m_mMonitorRenderResources[pMonitor].blurFBDirty || !*PBLUR) + if (!**PBLURNEWOPTIMIZE || !m_mMonitorRenderResources[pMonitor].blurFBDirty || !**PBLUR) return; // ignore if solitary present, nothing to blur @@ -1260,7 +1313,7 @@ void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) { bool hasWindows = false; for (auto& w : g_pCompositor->m_vWindows) { - if (w->m_iWorkspaceID == pMonitor->activeWorkspace && !w->isHidden() && w->m_bIsMapped && (!w->m_bIsFloating || *PBLURXRAY)) { + if (w->m_iWorkspaceID == pMonitor->activeWorkspace && !w->isHidden() && w->m_bIsMapped && (!w->m_bIsFloating || **PBLURXRAY)) { // check if window is valid if (!windowShouldBeBlurred(w.get())) @@ -1333,15 +1386,15 @@ void CHyprOpenGLImpl::preWindowPass() { } bool CHyprOpenGLImpl::preBlurQueued() { - static auto* const PBLURNEWOPTIMIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations")->intValue; - static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; + static auto* const PBLURNEWOPTIMIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations"); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); - return !(!m_RenderData.pCurrentMonData->blurFBDirty || !*PBLURNEWOPTIMIZE || !*PBLUR || !m_RenderData.pCurrentMonData->blurFBShouldRender); + return !(!m_RenderData.pCurrentMonData->blurFBDirty || !**PBLURNEWOPTIMIZE || !**PBLUR || !m_RenderData.pCurrentMonData->blurFBShouldRender); } bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(SLayerSurface* pLayer, CWindow* pWindow) { - static auto* const PBLURNEWOPTIMIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations")->intValue; - static auto* const PBLURXRAY = &g_pConfigManager->getConfigValuePtr("decoration:blur:xray")->intValue; + static auto* const PBLURNEWOPTIMIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:new_optimizations"); + static auto* const PBLURXRAY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:xray"); if (!m_RenderData.pCurrentMonData->blurFB.m_cTex.m_iTexID) return false; @@ -1352,7 +1405,7 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(SLayerSurface* pLayer, CWind if (pLayer && pLayer->xray == 0) return false; - if ((*PBLURNEWOPTIMIZE && pWindow && !pWindow->m_bIsFloating && !g_pCompositor->isWorkspaceSpecial(pWindow->m_iWorkspaceID)) || *PBLURXRAY) + if ((**PBLURNEWOPTIMIZE && pWindow && !pWindow->m_bIsFloating && !g_pCompositor->isWorkspaceSpecial(pWindow->m_iWorkspaceID)) || **PBLURXRAY) return true; if ((pLayer && pLayer->xray == 1) || (pWindow && pWindow->m_sAdditionalConfigData.xray.toUnderlying() == 1)) @@ -1364,8 +1417,8 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(SLayerSurface* pLayer, CWind void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, float a, wlr_surface* pSurface, int round, bool blockBlurOptimization, float blurA) { RASSERT(m_RenderData.pMonitor, "Tried to render texture with blur without begin()!"); - static auto* const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; - static auto* const PNOBLUROVERSIZED = &g_pConfigManager->getConfigValuePtr("decoration:no_blur_on_oversized")->intValue; + static auto* const PBLURENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + static auto* const PNOBLUROVERSIZED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:no_blur_on_oversized"); TRACY_GPU_ZONE("RenderTextureWithBlur"); @@ -1376,7 +1429,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo if (texDamage.empty()) return; - if (*PBLURENABLED == 0 || (*PNOBLUROVERSIZED && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) || + if (**PBLURENABLED == 0 || (**PNOBLUROVERSIZED && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) || (m_pCurrentWindow && (m_pCurrentWindow->m_sAdditionalConfigData.forceNoBlur || m_pCurrentWindow->m_sAdditionalConfigData.forceRGBX))) { renderTexture(tex, pBox, a, round, false, true); return; @@ -1437,11 +1490,11 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo // stencil done. Render everything. CBox MONITORBOX = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; // render our great blurred FB - static auto* const PBLURIGNOREOPACITY = &g_pConfigManager->getConfigValuePtr("decoration:blur:ignore_opacity")->intValue; + static auto* const PBLURIGNOREOPACITY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:ignore_opacity"); m_bEndFrame = true; // fix transformed const auto SAVEDRENDERMODIF = m_RenderData.renderModif; m_RenderData.renderModif = {}; // fix shit - renderTextureInternalWithDamage(POUTFB->m_cTex, &MONITORBOX, *PBLURIGNOREOPACITY ? blurA : a * blurA, &texDamage, 0, false, false, false); + renderTextureInternalWithDamage(POUTFB->m_cTex, &MONITORBOX, **PBLURIGNOREOPACITY ? blurA : a * blurA, &texDamage, 0, false, false, false); m_bEndFrame = false; m_RenderData.renderModif = SAVEDRENDERMODIF; @@ -1475,14 +1528,14 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in return; CBox newBox = *box; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); box = &newBox; if (borderSize < 1) return; - int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale * m_RenderData.renderModif.scale); + int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale); // adjust box box->x -= scaledBorderSize; @@ -1493,7 +1546,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in round += round == 0 ? 0 : scaledBorderSize; float matrix[9]; - wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0, + wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot, m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here float glMatrix[9]; @@ -1591,8 +1644,9 @@ void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFra // will try to copy the bg to apply blur. // this isn't entirely correct, but like, oh well. // small todo: maybe make this correct? :P - const auto BLURVAL = g_pConfigManager->getInt("decoration:blur:enabled"); - g_pConfigManager->setInt("decoration:blur:enabled", 0); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + const auto BLURVAL = **PBLUR; + **PBLUR = 0; // TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd. glViewport(0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); @@ -1603,7 +1657,7 @@ void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFra g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, false, RENDER_PASS_ALL, true); - g_pConfigManager->setInt("decoration:blur:enabled", BLURVAL); + **PBLUR = BLURVAL; g_pHyprRenderer->endRender(); } @@ -1643,14 +1697,15 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { // will try to copy the bg to apply blur. // this isn't entirely correct, but like, oh well. // small todo: maybe make this correct? :P - const auto BLURVAL = g_pConfigManager->getInt("decoration:blur:enabled"); - g_pConfigManager->setInt("decoration:blur:enabled", 0); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + const auto BLURVAL = **PBLUR; + **PBLUR = 0; clear(CColor(0, 0, 0, 0)); // JIC g_pHyprRenderer->renderWindow(pWindow, PMONITOR, &now, !pWindow->m_bX11DoesntWantBorders, RENDER_PASS_ALL); - g_pConfigManager->setInt("decoration:blur:enabled", BLURVAL); + **PBLUR = BLURVAL; g_pHyprRenderer->endRender(); @@ -1701,7 +1756,7 @@ void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!"); const auto PWINDOW = *pWindow; - static auto* const PDIMAROUND = &g_pConfigManager->getConfigValuePtr("decoration:dim_around")->floatValue; + static auto* const PDIMAROUND = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_around"); auto it = m_mWindowFramebuffers.begin(); for (; it != m_mWindowFramebuffers.end(); it++) { @@ -1728,9 +1783,9 @@ void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; - if (*PDIMAROUND && (*pWindow)->m_sAdditionalConfigData.dimAround) { + if (**PDIMAROUND && (*pWindow)->m_sAdditionalConfigData.dimAround) { CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y}; - g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, *PDIMAROUND * PWINDOW->m_fAlpha.fl())); + g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, **PDIMAROUND * PWINDOW->m_fAlpha.fl())); g_pHyprRenderer->damageMonitor(PMONITOR); } @@ -1779,18 +1834,18 @@ void CHyprOpenGLImpl::renderRoundedShadow(CBox* box, int round, int range, const TRACY_GPU_ZONE("RenderShadow"); CBox newBox = *box; - newBox.scale(m_RenderData.renderModif.scale).translate(m_RenderData.renderModif.translate); + m_RenderData.renderModif.applyToBox(newBox); box = &newBox; - static auto* const PSHADOWPOWER = &g_pConfigManager->getConfigValuePtr("decoration:shadow_render_power")->intValue; + static auto* const PSHADOWPOWER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:shadow_render_power"); - const auto SHADOWPOWER = std::clamp((int)*PSHADOWPOWER, 1, 4); + const auto SHADOWPOWER = std::clamp((int)**PSHADOWPOWER, 1, 4); const auto col = color; float matrix[9]; - wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0, + wlr_matrix_project_box(matrix, box->pWlr(), wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), newBox.rot, m_RenderData.pMonitor->projMatrix.data()); // TODO: write own, don't use WLR here float glMatrix[9]; @@ -1876,18 +1931,24 @@ void CHyprOpenGLImpl::renderMirrored() { renderTexture(PFB->m_cTex, &monbox, 1.f, 0, false, false); } -void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY) { - cairo_select_font_face(CAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); +void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { + static auto* const PSPLASHCOLOR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:col.splash"); - const auto FONTSIZE = (int)(m_RenderData.pMonitor->vecPixelSize.y / 76); + static auto* const PSPLASHFONT = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("misc:splash_font_family"); + + cairo_select_font_face(CAIRO, *PSPLASHFONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + const auto FONTSIZE = (int)(size.y / 76); cairo_set_font_size(CAIRO, FONTSIZE); - cairo_set_source_rgba(CAIRO, 1.0, 1.0, 1.0, 0.32); + const auto COLOR = CColor(**PSPLASHCOLOR); + + cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); cairo_text_extents_t textExtents; cairo_text_extents(CAIRO, g_pCompositor->m_szCurrentSplash.c_str(), &textExtents); - cairo_move_to(CAIRO, (m_RenderData.pMonitor->vecPixelSize.x - textExtents.width) / 2.0, m_RenderData.pMonitor->vecPixelSize.y - textExtents.height + offsetY); + cairo_move_to(CAIRO, (size.x - textExtents.width) / 2.0, size.y - textExtents.height + offsetY); cairo_show_text(CAIRO, g_pCompositor->m_szCurrentSplash.c_str()); @@ -1897,111 +1958,125 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); - static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; - static auto* const PNOSPLASH = &g_pConfigManager->getConfigValuePtr("misc:disable_splash_rendering")->intValue; - static auto* const PFORCEHYPRCHAN = &g_pConfigManager->getConfigValuePtr("misc:force_hypr_chan")->intValue; - static auto* const PFORCEWALLPAPER = &g_pConfigManager->getConfigValuePtr("misc:force_default_wallpaper")->intValue; + static auto* const PRENDERTEX = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo"); + static auto* const PNOSPLASH = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:disable_splash_rendering"); + static auto* const PFORCEWALLPAPER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:force_default_wallpaper"); - const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); + const auto FORCEWALLPAPER = std::clamp(**PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); - if (*PRENDERTEX) + static std::string texPath = ""; + + if (**PRENDERTEX) return; // release the last tex if exists - const auto PTEX = &m_mMonitorBGTextures[pMonitor]; - PTEX->destroyTexture(); + const auto PFB = &m_mMonitorBGFBs[pMonitor]; + PFB->release(); - PTEX->allocate(); + PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); Debug::log(LOG, "Allocated texture for BGTex"); // TODO: use relative paths to the installation // or configure the paths at build time - std::string texPath = "/usr/share/hyprland/wall_"; - std::string prefixes[] = {"", "anime_", "anime2_"}; + if (texPath.empty()) { + texPath = "/usr/share/hyprland/wall"; - // get the adequate tex - if (FORCEWALLPAPER == -1) { - std::random_device dev; - std::mt19937 engine(dev()); - std::uniform_int_distribution<> distribution(0, 2); - std::uniform_int_distribution<> distribution_anime(1, 2); + // get the adequate tex + if (FORCEWALLPAPER == -1) { + std::mt19937_64 engine(time(nullptr)); + std::uniform_int_distribution<> distribution(0, 2); - if (PFORCEHYPRCHAN) - texPath += prefixes[distribution_anime(engine)]; - else - texPath += prefixes[distribution(engine)]; - } else - texPath += prefixes[FORCEWALLPAPER]; + texPath += std::to_string(distribution(engine)); + } else + texPath += std::to_string(std::clamp(**PFORCEWALLPAPER, (int64_t)0, (int64_t)2)); - Vector2D textureSize; - if (pMonitor->vecTransformedSize.x > 3850) { - textureSize = Vector2D(7680, 4320); - texPath += "8K.png"; - } else if (pMonitor->vecTransformedSize.x > 1930) { - textureSize = Vector2D(3840, 2160); - texPath += "4K.png"; - } else { - textureSize = Vector2D(1920, 1080); - texPath += "2K.png"; + texPath += ".png"; + + // check if wallpapers exist + if (!std::filesystem::exists(texPath)) { + // try local + texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5); + + if (!std::filesystem::exists(texPath)) + return; // the texture will be empty, oh well. We'll clear with a solid color anyways. + } } - // check if wallpapers exist - if (!std::filesystem::exists(texPath)) { - // try local - texPath = texPath.substr(0, 5) + "local/" + texPath.substr(5); + // create a new one with cairo + CTexture tex; - if (!std::filesystem::exists(texPath)) - return; // the texture will be empty, oh well. We'll clear with a solid color anyways. - } + const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str()); + const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE); - PTEX->m_vSize = textureSize; + tex.allocate(); + const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; // calc the target box const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; - const double WPRATIO = 1.77; + const double WPRATIO = IMAGESIZE.x / IMAGESIZE.y; Vector2D origin; double scale; if (MONRATIO > WPRATIO) { - scale = m_RenderData.pMonitor->vecTransformedSize.x / PTEX->m_vSize.x; + scale = m_RenderData.pMonitor->vecTransformedSize.x / IMAGESIZE.x; - origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - PTEX->m_vSize.y * scale) / 2.0; + origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - IMAGESIZE.y * scale) / 2.0; } else { - scale = m_RenderData.pMonitor->vecTransformedSize.y / PTEX->m_vSize.y; + scale = m_RenderData.pMonitor->vecTransformedSize.y / IMAGESIZE.y; - origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - PTEX->m_vSize.x * scale) / 2.0; + origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - IMAGESIZE.x * scale) / 2.0; } - CBox box = {origin.x, origin.y, PTEX->m_vSize.x * scale, PTEX->m_vSize.y * scale}; + const Vector2D scaledSize = IMAGESIZE * scale; - m_mMonitorRenderResources[pMonitor].backgroundTexBox = box; + const auto CAIROSURFACE = cairo_image_surface_create(CAIROFORMAT, scaledSize.x, scaledSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); - // create a new one with cairo - const auto CAIROSURFACE = cairo_image_surface_create_from_png(texPath.c_str()); - const auto CAIRO = cairo_create(CAIROSURFACE); + cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD); + cairo_scale(CAIRO, scale, scale); + cairo_rectangle(CAIRO, 0, 0, 100, 100); + cairo_set_source_surface(CAIRO, CAIROISURFACE, 0, 0); + cairo_paint(CAIRO); - // scale it to fit the current monitor - cairo_scale(CAIRO, textureSize.x / pMonitor->vecTransformedSize.x, textureSize.y / pMonitor->vecTransformedSize.y); + if (!**PNOSPLASH) + renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO * scale, IMAGESIZE); - // render splash on wallpaper - if (!*PNOSPLASH) - renderSplash(CAIRO, CAIROSURFACE, origin.y * WPRATIO / MONRATIO); + cairo_surface_flush(CAIROSURFACE); + + CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale}; + tex.m_vSize = IMAGESIZE * scale; // copy the data to an OpenGL texture we have - const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - glBindTexture(GL_TEXTURE_2D, PTEX->m_iTexID); + const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; + const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; + const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); #ifndef GLES2 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + } #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.x, textureSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex.m_vSize.x, tex.m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); + cairo_surface_destroy(CAIROISURFACE); cairo_destroy(CAIRO); + // render the texture to our fb + PFB->bind(); + CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; + renderTextureInternalWithDamage(tex, &box, 1.0, &fakeDamage); + + // bind back + if (m_RenderData.currentFB) + m_RenderData.currentFB->bind(); + Debug::log(LOG, "Background created for monitor {}", pMonitor->szName); } @@ -2010,15 +2085,19 @@ void CHyprOpenGLImpl::clearWithTex() { TRACY_GPU_ZONE("RenderClearWithTex"); - auto TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor); + auto TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); - if (TEXIT == m_mMonitorBGTextures.end()) { + if (TEXIT == m_mMonitorBGFBs.end()) { createBGTextureForMonitor(m_RenderData.pMonitor); - TEXIT = m_mMonitorBGTextures.find(m_RenderData.pMonitor); + TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); } - if (TEXIT != m_mMonitorBGTextures.end()) - renderTexturePrimitive(TEXIT->second, &m_mMonitorRenderResources[m_RenderData.pMonitor].backgroundTexBox); + if (TEXIT != m_mMonitorBGFBs.end()) { + CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; + m_bEndFrame = true; + renderTexture(TEXIT->second.m_cTex, &monbox, 1); + m_bEndFrame = false; + } } void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) { @@ -2036,10 +2115,10 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) { g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT); } - auto TEXIT = g_pHyprOpenGL->m_mMonitorBGTextures.find(pMonitor); - if (TEXIT != g_pHyprOpenGL->m_mMonitorBGTextures.end()) { - TEXIT->second.destroyTexture(); - g_pHyprOpenGL->m_mMonitorBGTextures.erase(TEXIT); + auto TEXIT = g_pHyprOpenGL->m_mMonitorBGFBs.find(pMonitor); + if (TEXIT != g_pHyprOpenGL->m_mMonitorBGFBs.end()) { + TEXIT->second.release(); + g_pHyprOpenGL->m_mMonitorBGFBs.erase(TEXIT); } Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName); @@ -2210,4 +2289,27 @@ const SGLPixelFormat* CHyprOpenGLImpl::getPixelFormatFromDRM(uint32_t drmFormat) } return nullptr; -} \ No newline at end of file +} + +void SRenderModifData::applyToBox(CBox& box) { + for (auto& [type, val] : modifs) { + try { + switch (type) { + case RMOD_TYPE_SCALE: box.scale(std::any_cast(val)); break; + case RMOD_TYPE_SCALECENTER: box.scaleFromCenter(std::any_cast(val)); break; + case RMOD_TYPE_TRANSLATE: box.translate(std::any_cast(val)); break; + case RMOD_TYPE_ROTATE: box.rot += std::any_cast(val); break; + case RMOD_TYPE_ROTATECENTER: { + const auto THETA = std::any_cast(val); + const double COS = std::cos(THETA); + const double SIN = std::sin(THETA); + box.rot += THETA; + const auto OLDPOS = box.pos(); + box.x = OLDPOS.x * COS - OLDPOS.y * SIN; + box.y = OLDPOS.y * COS + OLDPOS.x * SIN; + } + } + } catch (std::bad_any_cast& e) { Debug::log(ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToBox!"); } + } +} + diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index cd52fe3e..1e43333b 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -36,8 +36,17 @@ enum eDiscardMode { }; struct SRenderModifData { - Vector2D translate = {}; - float scale = 1.f; + enum eRenderModifType { + RMOD_TYPE_SCALE, /* scale by a float */ + RMOD_TYPE_SCALECENTER, /* scale by a float from the center */ + RMOD_TYPE_TRANSLATE, /* translate by a Vector2D */ + RMOD_TYPE_ROTATE, /* rotate by a float in rad from top left */ + RMOD_TYPE_ROTATECENTER, /* rotate by a float in rad from center */ + }; + + std::vector> modifs; + + void applyToBox(CBox& box); }; struct SGLPixelFormat { @@ -62,8 +71,6 @@ struct SMonitorRenderData { bool blurFBDirty = true; bool blurFBShouldRender = false; - CBox backgroundTexBox; - // Shaders bool m_bShadersInitialized = false; CShader m_shQUAD; @@ -94,11 +101,13 @@ struct SCurrentRenderData { CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc) CRegion damage; + CRegion finalDamage; // damage used for funal off -> main SRenderModifData renderModif; float mouseZoomFactor = 1.f; bool mouseZoomUseMouse = true; // true by default bool useNearestNeighbor = false; + bool forceIntrospection = false; // cleaned in ::end() Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); @@ -115,7 +124,7 @@ class CHyprOpenGLImpl { public: CHyprOpenGLImpl(); - void begin(CMonitor*, CRegion*, CFramebuffer* fb = nullptr /* if provided, it's not a real frame */); + void begin(CMonitor*, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); void end(); void renderRect(CBox*, const CColor&, int round = 0); @@ -166,6 +175,8 @@ class CHyprOpenGLImpl { void renderOffToMain(CFramebuffer* off); void bindBackOnMain(); + void setDamage(const CRegion& damage, std::optional finalDamage = {}); + uint32_t getPreferredReadFormat(CMonitor* pMonitor); const SGLPixelFormat* getPixelFormatFromDRM(uint32_t drmFormat); @@ -181,7 +192,7 @@ class CHyprOpenGLImpl { std::unordered_map m_mWindowFramebuffers; std::unordered_map m_mLayerFramebuffers; std::unordered_map m_mMonitorRenderResources; - std::unordered_map m_mMonitorBGTextures; + std::unordered_map m_mMonitorBGFBs; struct { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; @@ -219,7 +230,7 @@ class CHyprOpenGLImpl { void renderTextureInternalWithDamage(const CTexture&, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false, bool allowCustomUV = false, bool allowDim = false); void renderTexturePrimitive(const CTexture& tex, CBox* pBox); - void renderSplash(cairo_t* const, cairo_surface_t* const, double); + void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); void preBlurForCurrentMonitor(); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 88320139..1c3aaab0 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -9,8 +9,6 @@ extern "C" { } CHyprRenderer::CHyprRenderer() { - if (envEnabled("WLR_DRM_NO_ATOMIC")) - m_bTearingEnvSatisfied = true; if (g_pCompositor->m_sWLRSession) { wlr_device* dev; @@ -50,8 +48,8 @@ CHyprRenderer::CHyprRenderer() { } static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) { - static auto* const PBLURPOPUPS = &g_pConfigManager->getConfigValuePtr("decoration:blur:popups")->intValue; - static auto* const PBLURPOPUPSIGNOREALPHA = &g_pConfigManager->getConfigValuePtr("decoration:blur:popups_ignorealpha")->floatValue; + static auto* const PBLURPOPUPS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:popups"); + static auto* const PBLURPOPUPSIGNOREALPHA = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:popups_ignorealpha"); const auto TEXTURE = wlr_surface_get_texture(surface); const auto RDATA = (SRenderData*)data; @@ -107,18 +105,22 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) if (windowBox.width <= 1 || windowBox.height <= 1) return; // invisible - g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface); - windowBox.scale(RDATA->pMonitor->scale); windowBox.round(); + const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && + windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ && + DELTALESSTHAN(windowBox.width, surface->current.buffer_width, 3) && DELTALESSTHAN(windowBox.height, surface->current.buffer_height, 3) /* off by one-or-two */ && + (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; + + g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1); + // check for fractional scale surfaces misaligning the buffer size // in those cases it's better to just force nearest neighbor - // as long as the window is not animated. During those it'd look weird + // as long as the window is not animated. During those it'd look weird. + // UV will fixup it as well const auto NEARESTNEIGHBORSET = g_pHyprOpenGL->m_RenderData.useNearestNeighbor; - if (std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && - windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ && - (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */) + if (MISALIGNEDFSV1) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; float rounding = RDATA->rounding; @@ -146,11 +148,11 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, rounding, true); } } else { - if (RDATA->blur && RDATA->popup && *PBLURPOPUPS) { + if (RDATA->blur && RDATA->popup && **PBLURPOPUPS) { - if (*PBLURPOPUPSIGNOREALPHA != 1.f) { + if (**PBLURPOPUPSIGNOREALPHA != 1.f) { g_pHyprOpenGL->m_RenderData.discardMode |= DISCARD_ALPHA; - g_pHyprOpenGL->m_RenderData.discardOpacity = *PBLURPOPUPSIGNOREALPHA; + g_pHyprOpenGL->m_RenderData.discardOpacity = **PBLURPOPUPSIGNOREALPHA; } g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, surface, rounding, true); @@ -161,7 +163,7 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { wlr_surface_send_frame_done(surface, RDATA->when); - wlr_presentation_surface_textured_on_output(g_pCompositor->m_sWLRPresentation, surface, RDATA->pMonitor->output); + wlr_presentation_surface_textured_on_output(surface, RDATA->pMonitor->output); } g_pHyprOpenGL->blend(true); @@ -289,11 +291,14 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(CMonitor* pMonitor, CWorksp continue; } - if (w->m_iWorkspaceID != pMonitor->activeWorkspace || !w->m_bIsFullscreen) + if (!w->m_bIsFullscreen) continue; renderWindow(w.get(), pMonitor, time, pWorkspace->m_efFullscreenMode != FULLSCREEN_FULL, RENDER_PASS_ALL); + if (w->m_iWorkspaceID != pWorkspace->m_iID) + continue; + pWorkspaceWindow = w.get(); } @@ -404,8 +409,8 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); const auto REALPOS = pWindow->m_vRealPosition.vec() + (pWindow->m_bPinned ? Vector2D{} : PWORKSPACE->m_vRenderOffset.vec()); - static auto* const PDIMAROUND = &g_pConfigManager->getConfigValuePtr("decoration:dim_around")->floatValue; - static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; + static auto* const PDIMAROUND = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_around"); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); SRenderData renderdata = {pMonitor, time}; CBox textureBox = {REALPOS.x, REALPOS.y, std::max(pWindow->m_vRealSize.vec().x, 5.0), std::max(pWindow->m_vRealSize.vec().y, 5.0)}; @@ -447,12 +452,12 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* if (*PDIMAROUND && pWindow->m_sAdditionalConfigData.dimAround && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) { CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y}; - g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, *PDIMAROUND * renderdata.alpha * renderdata.fadeAlpha)); + g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, **PDIMAROUND * renderdata.alpha * renderdata.fadeAlpha)); } // clip box for animated offsets const Vector2D PREOFFSETPOS = {renderdata.x, renderdata.y}; - if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned) { + if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->m_bPinned && !pWindow->m_bIsFullscreen) { Vector2D offset; if (PWORKSPACE->m_vRenderOffset.vec().x != 0) { @@ -516,11 +521,11 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* } } - static auto* const PXWLUSENN = &g_pConfigManager->getConfigValuePtr("xwayland:use_nearest_neighbor")->intValue; - if ((pWindow->m_bIsX11 && *PXWLUSENN) || pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying()) + static auto* const PXWLUSENN = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:use_nearest_neighbor"); + if ((pWindow->m_bIsX11 && **PXWLUSENN) || pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying()) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; - if (pWindow->m_pWLSurface.small() && !pWindow->m_pWLSurface.m_bFillIgnoreSmall && renderdata.blur && *PBLUR) { + if (pWindow->m_pWLSurface.small() && !pWindow->m_pWLSurface.m_bFillIgnoreSmall && renderdata.blur && **PBLUR) { CBox wb = {renderdata.x - pMonitor->vecPosition.x, renderdata.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h}; wb.scale(pMonitor->scale).round(); g_pHyprOpenGL->renderRectWithBlur(&wb, CColor(0, 0, 0, 0), renderdata.dontRound ? 0 : renderdata.rounding - 1, renderdata.fadeAlpha, @@ -652,13 +657,17 @@ void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMon } void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* pWorkspace, timespec* time, const Vector2D& translate, const float& scale) { - static auto* const PDIMSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:dim_special")->floatValue; - static auto* const PBLURSPECIAL = &g_pConfigManager->getConfigValuePtr("decoration:blur:special")->intValue; - static auto* const PBLUR = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; - static auto* const PRENDERTEX = &g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo")->intValue; - static auto* const PBACKGROUNDCOLOR = &g_pConfigManager->getConfigValuePtr("misc:background_color")->intValue; + static auto* const PDIMSPECIAL = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:dim_special"); + static auto* const PBLURSPECIAL = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:special"); + static auto* const PBLUR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + static auto* const PRENDERTEX = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:disable_hyprland_logo"); + static auto* const PBACKGROUNDCOLOR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:background_color"); - const SRenderModifData RENDERMODIFDATA = {translate, scale}; + SRenderModifData RENDERMODIFDATA; + if (translate != Vector2D{0, 0}) + RENDERMODIFDATA.modifs.push_back({SRenderModifData::eRenderModifType::RMOD_TYPE_TRANSLATE, translate}); + if (scale != 1.f) + RENDERMODIFDATA.modifs.push_back({SRenderModifData::eRenderModifType::RMOD_TYPE_SCALE, scale}); if (!pMonitor) return; @@ -690,8 +699,8 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* g_pHyprOpenGL->blend(false); if (!canSkipBackBufferClear(pMonitor)) { - if (*PRENDERTEX /* inverted cfg flag */) - g_pHyprOpenGL->clear(CColor(*PBACKGROUNDCOLOR)); + if (**PRENDERTEX /* inverted cfg flag */) + g_pHyprOpenGL->clear(CColor(**PBACKGROUNDCOLOR)); else g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" } @@ -727,12 +736,12 @@ void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, CWorkspace* const auto SPECIALANIMPROGRS = ws->m_vRenderOffset.isBeingAnimated() ? ws->m_vRenderOffset.getCurveValue() : ws->m_fAlpha.getCurveValue(); const bool ANIMOUT = !pMonitor->specialWorkspaceID; - if (*PDIMSPECIAL != 0.f) { + if (**PDIMSPECIAL != 0.f) { CBox monbox = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale}; - g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, *PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS))); + g_pHyprOpenGL->renderRect(&monbox, CColor(0, 0, 0, **PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS))); } - if (*PBLURSPECIAL && *PBLUR) { + if (**PBLURSPECIAL && **PBLUR) { CBox monbox = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale}; g_pHyprOpenGL->renderRectWithBlur(&monbox, CColor(0, 0, 0, 0), 0, (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS)); } @@ -800,7 +809,7 @@ void CHyprRenderer::renderLockscreen(CMonitor* pMonitor, timespec* now) { } } -void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main) { +void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurface, bool main, const Vector2D& projSize, bool fixMisalignedFSV1) { if (!pWindow || !pWindow->m_bIsX11) { Vector2D uvTL; Vector2D uvBR = Vector2D(1, 1); @@ -822,6 +831,15 @@ void CHyprRenderer::calculateUVForSurface(CWindow* pWindow, wlr_surface* pSurfac } } + if (projSize != Vector2D{} && fixMisalignedFSV1) { + // instead of nearest_neighbor (we will repeat / skip) + // just cut off / expand surface + const Vector2D PIXELASUV = Vector2D{1, 1} / Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height}; + const Vector2D MISALIGNMENT = Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height} - projSize; + if (MISALIGNMENT != Vector2D{}) + uvBR -= MISALIGNMENT * PIXELASUV; + } + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL; g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR; @@ -897,17 +915,17 @@ bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { return false; // finally, we should be GTG. - wlr_output_attach_buffer(pMonitor->output, &PSURFACE->buffer->base); + wlr_output_state_set_buffer(pMonitor->state.wlr(), &PSURFACE->buffer->base); - if (!wlr_output_test(pMonitor->output)) + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) return false; timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_surface_send_frame_done(PSURFACE, &now); - wlr_presentation_surface_scanned_out_on_output(g_pCompositor->m_sWLRPresentation, PSURFACE, pMonitor->output); + wlr_presentation_surface_scanned_out_on_output(PSURFACE, pMonitor->output); - if (wlr_output_commit(pMonitor->output)) { + if (pMonitor->state.commit()) { if (!m_pLastScanout) { m_pLastScanout = PCANDIDATE; Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE, PCANDIDATE->m_szTitle); @@ -925,23 +943,23 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now(); - static auto* const PDEBUGOVERLAY = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue; - static auto* const PDAMAGETRACKINGMODE = &g_pConfigManager->getConfigValuePtr("debug:damage_tracking")->intValue; - static auto* const PDAMAGEBLINK = &g_pConfigManager->getConfigValuePtr("debug:damage_blink")->intValue; - static auto* const PNODIRECTSCANOUT = &g_pConfigManager->getConfigValuePtr("misc:no_direct_scanout")->intValue; - static auto* const PVFR = &g_pConfigManager->getConfigValuePtr("misc:vfr")->intValue; - static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue; - static auto* const PANIMENABLED = &g_pConfigManager->getConfigValuePtr("animations:enabled")->intValue; - static auto* const PFIRSTLAUNCHANIM = &g_pConfigManager->getConfigValuePtr("animations:first_launch_animation")->intValue; - static auto* const PTEARINGENABLED = &g_pConfigManager->getConfigValuePtr("general:allow_tearing")->intValue; + static auto* const PDEBUGOVERLAY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:overlay"); + static auto* const PDAMAGETRACKINGMODE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:damage_tracking"); + static auto* const PDAMAGEBLINK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:damage_blink"); + static auto* const PNODIRECTSCANOUT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:no_direct_scanout"); + static auto* const PVFR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:vfr"); + static auto* const PZOOMFACTOR = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor"); + static auto* const PANIMENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("animations:enabled"); + static auto* const PFIRSTLAUNCHANIM = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("animations:first_launch_animation"); + static auto* const PTEARINGENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:allow_tearing"); static int damageBlinkCleanup = 0; // because double-buffered - if (!*PDAMAGEBLINK) + if (!**PDAMAGEBLINK) damageBlinkCleanup = 0; static bool firstLaunch = true; - static bool firstLaunchAnimActive = *PFIRSTLAUNCHANIM; + static bool firstLaunchAnimActive = **PFIRSTLAUNCHANIM; float zoomInFactorFirstLaunch = 1.f; @@ -951,7 +969,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } if (m_tRenderTimer.getSeconds() < 1.5f && firstLaunchAnimActive) { // TODO: make the animation system more damage-flexible so that this can be migrated to there - if (!*PANIMENABLED) { + if (!**PANIMENABLED) { zoomInFactorFirstLaunch = 1.f; firstLaunchAnimActive = false; } else { @@ -964,18 +982,17 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { renderStart = std::chrono::high_resolution_clock::now(); - if (*PDEBUGOVERLAY == 1) { + if (**PDEBUGOVERLAY == 1) g_pDebugOverlay->frameData(pMonitor); - } if (pMonitor->framesToSkip > 0) { pMonitor->framesToSkip -= 1; if (!pMonitor->noFrameSchedule) g_pCompositor->scheduleFrameForMonitor(pMonitor); - else { + else Debug::log(LOG, "NoFrameSchedule hit for {}.", pMonitor->szName); - } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); if (pMonitor->framesToSkip > 10) @@ -985,7 +1002,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { // checks // if (pMonitor->ID == m_pMostHzMonitor->ID || - *PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that + **PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that g_pCompositor->sanityCheckWorkspaces(); g_pConfigManager->dispatchExecOnce(); // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd @@ -1008,14 +1025,15 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { const auto PGAMMACTRL = wlr_gamma_control_manager_v1_get_control(g_pCompositor->m_sWLRGammaCtrlMgr, pMonitor->output); - if (!wlr_gamma_control_v1_apply(PGAMMACTRL, &pMonitor->output->pending)) { + if (!wlr_gamma_control_v1_apply(PGAMMACTRL, pMonitor->state.wlr())) { Debug::log(ERR, "Could not apply gamma control to {}", pMonitor->szName); return; } - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Output test failed for setting gamma to {}", pMonitor->szName); - wlr_output_rollback(pMonitor->output); + // aka rollback + wlr_gamma_control_v1_apply(nullptr, pMonitor->state.wlr()); wlr_gamma_control_v1_send_failed_and_destroy(PGAMMACTRL); } } @@ -1025,7 +1043,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { if (pMonitor->tearingState.nextRenderTorn) { pMonitor->tearingState.nextRenderTorn = false; - if (!*PTEARINGENABLED) { + if (!**PTEARINGENABLED) { Debug::log(WARN, "Tearing commit requested but the master switch general:allow_tearing is off, ignoring"); return; } @@ -1044,7 +1062,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { shouldTear = true; } - if (!*PNODIRECTSCANOUT && !shouldTear) { + if (!**PNODIRECTSCANOUT && !shouldTear) { if (attemptDirectScanout(pMonitor)) { return; } else if (m_pLastScanout) { @@ -1064,13 +1082,12 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { clock_gettime(CLOCK_MONOTONIC, &now); // check the damage - CRegion damage; - bool hasChanged = pMonitor->output->needs_frame || pixman_region32_not_empty(&pMonitor->damage.current); + bool hasChanged = pMonitor->output->needs_frame || pixman_region32_not_empty(&pMonitor->damage.current); - if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0) + if (!hasChanged && **PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0) return; - if (*PDAMAGETRACKINGMODE == -1) { + if (**PDAMAGETRACKINGMODE == -1) { Debug::log(CRIT, "Damage tracking mode -1 ????"); return; } @@ -1081,54 +1098,18 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, true); - wlr_damage_ring_get_buffer_damage(&pMonitor->damage, m_iLastBufferAge, damage.pixman()); - pMonitor->renderingActive = true; // we need to cleanup fading out when rendering the appropriate context g_pCompositor->cleanupFadingOut(pMonitor->ID); - // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0 || - pMonitor->isMirror() /* why??? */) { - - damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; - pMonitor->lastFrameDamage = damage; - } else { - static auto* const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")->intValue; - - // if we use blur we need to expand the damage for proper blurring - if (*PBLURENABLED == 1) { - // TODO: can this be optimized? - static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur:size")->intValue; - static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur:passes")->intValue; - const auto BLURRADIUS = - *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. - - // now, prep the damage, get the extended damage region - wlr_region_expand(damage.pixman(), damage.pixman(), BLURRADIUS); // expand for proper blurring - - pMonitor->lastFrameDamage = damage; - - wlr_region_expand(damage.pixman(), damage.pixman(), BLURRADIUS); // expand for proper blurring 2 - } else { - pMonitor->lastFrameDamage = damage; - } - } - - if (pMonitor->forceFullFrames > 0) { - pMonitor->forceFullFrames -= 1; - if (pMonitor->forceFullFrames > 10) - pMonitor->forceFullFrames = 0; - } - // TODO: this is getting called with extents being 0,0,0,0 should it be? // potentially can save on resources. TRACY_GPU_ZONE("Render"); if (pMonitor == g_pCompositor->getMonitorFromCursor()) - g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY); + g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(**PZOOMFACTOR, 1.f, INFINITY); else g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f; @@ -1138,15 +1119,55 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; } + CRegion damage, finalDamage; if (!beginRender(pMonitor, damage, RENDER_MODE_NORMAL)) { Debug::log(ERR, "renderer: couldn't beginRender()!"); if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); + pMonitor->state.clear(); + return; } + // if we have no tracking or full tracking, invalidate the entire monitor + if (**PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || **PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0 || + pMonitor->isMirror() /* why??? */) { + + damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; + finalDamage = damage; + } else { + static auto* const PBLURENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:enabled"); + + // if we use blur we need to expand the damage for proper blurring + // if framebuffer was not offloaded we're not doing introspection aka not blurring so this is redundant and dumb + if (**PBLURENABLED == 1 && g_pHyprOpenGL->m_bOffloadedFramebuffer) { + // TODO: can this be optimized? + static auto* const PBLURSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:size"); + static auto* const PBLURPASSES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:blur:passes"); + const auto BLURRADIUS = + **PBLURPASSES > 10 ? pow(2, 15) : std::clamp(**PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, **PBLURPASSES); // is this 2^pass? I don't know but it works... I think. + + // now, prep the damage, get the extended damage region + wlr_region_expand(damage.pixman(), damage.pixman(), BLURRADIUS); // expand for proper blurring + + finalDamage = damage; + + wlr_region_expand(damage.pixman(), damage.pixman(), BLURRADIUS); // expand for proper blurring 2 + } else + finalDamage = damage; + } + + // update damage in renderdata as we modified it + g_pHyprOpenGL->setDamage(damage, finalDamage); + + if (pMonitor->forceFullFrames > 0) { + pMonitor->forceFullFrames -= 1; + if (pMonitor->forceFullFrames > 10) + pMonitor->forceFullFrames = 0; + } + EMIT_HOOK_EVENT("render", RENDER_BEGIN); bool renderCursor = true; @@ -1170,17 +1191,17 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } // for drawing the debug overlay - if (pMonitor == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) { + if (pMonitor == g_pCompositor->m_vMonitors.front().get() && **PDEBUGOVERLAY == 1) { renderStartOverlay = std::chrono::high_resolution_clock::now(); g_pDebugOverlay->draw(); endRenderOverlay = std::chrono::high_resolution_clock::now(); } - if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { + if (**PDAMAGEBLINK && damageBlinkCleanup == 0) { CBox monrect = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}; g_pHyprOpenGL->renderRect(&monrect, CColor(1.0, 0.0, 1.0, 100.0 / 255.0), 0); damageBlinkCleanup = 1; - } else if (*PDAMAGEBLINK) { + } else if (**PDAMAGEBLINK) { damageBlinkCleanup++; if (damageBlinkCleanup > 3) damageBlinkCleanup = 0; @@ -1195,7 +1216,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { if (renderCursor) { TRACY_GPU_ZONE("RenderCursor"); - bool lockSoftware = pMonitor == g_pCompositor->getMonitorFromCursor() && *PZOOMFACTOR != 1.f; + bool lockSoftware = pMonitor == g_pCompositor->getMonitorFromCursor() && **PZOOMFACTOR != 1.f; if (lockSoftware) { wlr_output_lock_software_cursors(pMonitor->output, true); @@ -1211,44 +1232,44 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { TRACY_GPU_COLLECT; - // calc frame damage - CRegion frameDamage{}; + if (!pMonitor->mirrors.empty()) { + CRegion frameDamage{}; - const auto TRANSFORM = wlr_output_transform_invert(pMonitor->output->transform); - wlr_region_transform(frameDamage.pixman(), pMonitor->lastFrameDamage.pixman(), TRANSFORM, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); + const auto TRANSFORM = wlr_output_transform_invert(pMonitor->output->transform); + wlr_region_transform(frameDamage.pixman(), finalDamage.pixman(), TRANSFORM, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) - frameDamage.add(0, 0, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); + if (**PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || **PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) + frameDamage.add(0, 0, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); - if (*PDAMAGEBLINK) - frameDamage.add(damage); + if (**PDAMAGEBLINK) + frameDamage.add(damage); - if (!pMonitor->mirrors.empty()) g_pHyprRenderer->damageMirrorsWith(pMonitor, frameDamage); + } pMonitor->renderingActive = false; EMIT_HOOK_EVENT("render", RENDER_POST); - pMonitor->output->pending.tearing_page_flip = shouldTear; + pMonitor->state.wlr()->tearing_page_flip = shouldTear; - if (!wlr_output_commit(pMonitor->output)) { + if (!pMonitor->state.commit()) { if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); + damageMonitor(pMonitor); + return; } if (shouldTear) pMonitor->tearingState.busy = true; - wlr_damage_ring_rotate(&pMonitor->damage); - if (UNLOCK_SC) wlr_output_lock_software_cursors(pMonitor->output, false); - if (*PDAMAGEBLINK || *PVFR == 0 || pMonitor->pendingFrame) + if (**PDAMAGEBLINK || **PVFR == 0 || pMonitor->pendingFrame) g_pCompositor->scheduleFrameForMonitor(pMonitor); pMonitor->pendingFrame = false; @@ -1256,7 +1277,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { const float µs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f; g_pDebugOverlay->renderData(pMonitor, µs); - if (*PDEBUGOVERLAY == 1) { + if (**PDEBUGOVERLAY == 1) { if (pMonitor == g_pCompositor->m_vMonitors.front().get()) { const float µsNoOverlay = µs - std::chrono::duration_cast(endRenderOverlay - renderStartOverlay).count() / 1000.f; g_pDebugOverlay->renderDataNoOverlay(pMonitor, µsNoOverlay); @@ -1325,11 +1346,13 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool if (!head->state.enabled) { commandForCfg += "disabled"; if (!test) - g_pConfigManager->parseKeyword("monitor", commandForCfg, true); + g_pConfigManager->parseKeyword("monitor", commandForCfg); continue; } - wlr_output_enable(OUTPUT, head->state.enabled); + const auto PMONITOR = g_pCompositor->getRealMonitorFromOutput(OUTPUT); + RASSERT(PMONITOR, "nullptr monitor in outputMgrApplyTest"); + wlr_output_state_set_enabled(PMONITOR->state.wlr(), head->state.enabled); if (head->state.mode) commandForCfg += @@ -1342,11 +1365,11 @@ void CHyprRenderer::outputMgrApplyTest(wlr_output_configuration_v1* config, bool std::to_string((int)head->state.transform); if (!test) { - g_pConfigManager->parseKeyword("monitor", commandForCfg, true); - wlr_output_state_set_adaptive_sync_enabled(&OUTPUT->pending, head->state.adaptive_sync_enabled); + g_pConfigManager->parseKeyword("monitor", commandForCfg); + wlr_output_state_set_adaptive_sync_enabled(PMONITOR->state.wlr(), head->state.adaptive_sync_enabled); } - ok = wlr_output_test(OUTPUT); + ok = wlr_output_test_state(OUTPUT, PMONITOR->state.wlr()); if (!ok) break; @@ -1567,39 +1590,39 @@ void CHyprRenderer::damageSurface(wlr_surface* pSurface, double x, double y, dou y += CORRECTION.y; } - CRegion damageBox; - wlr_surface_get_effective_damage(pSurface, damageBox.pixman()); + const auto WLSURF = CWLSurface::surfaceFromWlr(pSurface); + CRegion damageBox = WLSURF ? WLSURF->logicalDamage() : CRegion{}; + if (!WLSURF) { + Debug::log(ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); + wlr_surface_get_effective_damage(pSurface, damageBox.pixman()); + } if (scale != 1.0) - wlr_region_scale(damageBox.pixman(), damageBox.pixman(), scale); + damageBox.scale(scale); // schedule frame events - if (!wl_list_empty(&pSurface->current.frame_callback_list)) { + if (!wl_list_empty(&pSurface->current.frame_callback_list)) g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y))); - } if (damageBox.empty()) return; + damageBox.translate({x, y}); + CRegion damageBoxForEach; for (auto& m : g_pCompositor->m_vMonitors) { if (!m->output) continue; - double lx = 0, ly = 0; - wlr_output_layout_output_coords(g_pCompositor->m_sWLROutputLayout, m->output, &lx, &ly); - - damageBoxForEach = damageBox; - damageBoxForEach.translate({x - m->vecPosition.x, y - m->vecPosition.y}); - wlr_region_scale(damageBoxForEach.pixman(), damageBoxForEach.pixman(), m->scale); - damageBoxForEach.translate({lx + m->vecPosition.x, ly + m->vecPosition.y}); + damageBoxForEach.set(damageBox); + damageBoxForEach.translate({-m->vecPosition.x, -m->vecPosition.y}).scale(m->scale); m->addDamage(&damageBoxForEach); } - static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue; + static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage"); - if (*PLOGDAMAGE) + if (**PLOGDAMAGE) Debug::log(LOG, "Damage: Surface (extents): xy: {}, {} wh: {}, {}", damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y1, damageBox.pixman()->extents.x2 - damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y2 - damageBox.pixman()->extents.y1); } @@ -1618,9 +1641,9 @@ void CHyprRenderer::damageWindow(CWindow* pWindow) { for (auto& wd : pWindow->m_dWindowDecorations) wd->damageEntire(); - static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue; + static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage"); - if (*PLOGDAMAGE) + if (**PLOGDAMAGE) Debug::log(LOG, "Damage: Window ({}): xy: {}, {} wh: {}, {}", pWindow->m_szTitle, damageBox.x, damageBox.y, damageBox.width, damageBox.height); } @@ -1631,9 +1654,9 @@ void CHyprRenderer::damageMonitor(CMonitor* pMonitor) { CBox damageBox = {0, 0, INT16_MAX, INT16_MAX}; pMonitor->addDamage(&damageBox); - static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue; + static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage"); - if (*PLOGDAMAGE) + if (**PLOGDAMAGE) Debug::log(LOG, "Damage: Monitor {}", pMonitor->szName); } @@ -1650,9 +1673,9 @@ void CHyprRenderer::damageBox(CBox* pBox) { m->addDamage(&damageBox); } - static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue; + static auto* const PLOGDAMAGE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:log_damage"); - if (*PLOGDAMAGE) + if (**PLOGDAMAGE) Debug::log(LOG, "Damage: Box: xy: {}, {} wh: {}, {}", pBox->x, pBox->y, pBox->width, pBox->height); } @@ -1708,14 +1731,19 @@ DAMAGETRACKINGMODES CHyprRenderer::damageTrackingModeFromStr(const std::string& bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorRule, bool force) { - static auto* const PDISABLESCALECHECKS = &g_pConfigManager->getConfigValuePtr("debug:disable_scale_checks")->intValue; + static auto* const PDISABLESCALECHECKS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:disable_scale_checks"); Debug::log(LOG, "Applying monitor rule for {}", pMonitor->szName); pMonitor->activeMonitorRule = *pMonitorRule; + if (pMonitor->forceSize.has_value()) + pMonitor->activeMonitorRule.resolution = pMonitor->forceSize.value(); + + const auto RULE = &pMonitor->activeMonitorRule; + // if it's disabled, disable and ignore - if (pMonitorRule->disabled) { + if (RULE->disabled) { if (pMonitor->m_bEnabled) pMonitor->onDisconnect(); @@ -1734,56 +1762,59 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR // Check if the rule isn't already applied // TODO: clean this up lol - if (!force && DELTALESSTHAN(pMonitor->vecPixelSize.x, pMonitorRule->resolution.x, 1) && DELTALESSTHAN(pMonitor->vecPixelSize.y, pMonitorRule->resolution.y, 1) && - DELTALESSTHAN(pMonitor->refreshRate, pMonitorRule->refreshRate, 1) && pMonitor->setScale == pMonitorRule->scale && - ((DELTALESSTHAN(pMonitor->vecPosition.x, pMonitorRule->offset.x, 1) && DELTALESSTHAN(pMonitor->vecPosition.y, pMonitorRule->offset.y, 1)) || - pMonitorRule->offset == Vector2D(-INT32_MAX, -INT32_MAX)) && - pMonitor->transform == pMonitorRule->transform && pMonitorRule->enable10bit == pMonitor->enabled10bit && - !memcmp(&pMonitor->customDrmMode, &pMonitorRule->drmMode, sizeof(pMonitor->customDrmMode))) { + if (!force && DELTALESSTHAN(pMonitor->vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(pMonitor->vecPixelSize.y, RULE->resolution.y, 1) && + DELTALESSTHAN(pMonitor->refreshRate, RULE->refreshRate, 1) && pMonitor->setScale == RULE->scale && + ((DELTALESSTHAN(pMonitor->vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(pMonitor->vecPosition.y, RULE->offset.y, 1)) || + RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) && + pMonitor->transform == RULE->transform && RULE->enable10bit == pMonitor->enabled10bit && + !memcmp(&pMonitor->customDrmMode, &RULE->drmMode, sizeof(pMonitor->customDrmMode))) { Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", pMonitor->szName); return true; } + const auto WAS10B = pMonitor->enabled10bit; + const auto OLDRES = pMonitor->vecPixelSize; + // Needed in case we are switching from a custom modeline to a standard mode pMonitor->customDrmMode = {}; bool autoScale = false; - if (pMonitorRule->scale > 0.1) { - pMonitor->scale = pMonitorRule->scale; + if (RULE->scale > 0.1) { + pMonitor->scale = RULE->scale; } else { autoScale = true; const auto DEFAULTSCALE = pMonitor->getDefaultScale(); pMonitor->scale = DEFAULTSCALE; } - wlr_output_set_scale(pMonitor->output, pMonitor->scale); + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale); pMonitor->setScale = pMonitor->scale; - wlr_output_set_transform(pMonitor->output, pMonitorRule->transform); - pMonitor->transform = pMonitorRule->transform; + wlr_output_state_set_transform(pMonitor->state.wlr(), RULE->transform); + pMonitor->transform = RULE->transform; - const auto WLRREFRESHRATE = (wlr_backend_is_wl(pMonitor->output->backend) || wlr_backend_is_x11(pMonitor->output->backend)) ? 0 : pMonitorRule->refreshRate * 1000; + const auto WLRREFRESHRATE = (wlr_backend_is_wl(pMonitor->output->backend) || wlr_backend_is_x11(pMonitor->output->backend)) ? 0 : RULE->refreshRate * 1000; // loop over modes and choose an appropriate one. - if (pMonitorRule->resolution != Vector2D() && pMonitorRule->resolution != Vector2D(-1, -1) && pMonitorRule->resolution != Vector2D(-1, -2)) { - if (!wl_list_empty(&pMonitor->output->modes) && pMonitorRule->drmMode.type != DRM_MODE_TYPE_USERDEF) { + if (RULE->resolution != Vector2D() && RULE->resolution != Vector2D(-1, -1) && RULE->resolution != Vector2D(-1, -2)) { + if (!wl_list_empty(&pMonitor->output->modes) && RULE->drmMode.type != DRM_MODE_TYPE_USERDEF) { wlr_output_mode* mode; bool found = false; wl_list_for_each(mode, &pMonitor->output->modes, link) { // if delta of refresh rate, w and h chosen and mode is < 1 we accept it - if (DELTALESSTHAN(mode->width, pMonitorRule->resolution.x, 1) && DELTALESSTHAN(mode->height, pMonitorRule->resolution.y, 1) && - DELTALESSTHAN(mode->refresh / 1000.f, pMonitorRule->refreshRate, 1)) { - wlr_output_set_mode(pMonitor->output, mode); + if (DELTALESSTHAN(mode->width, RULE->resolution.x, 1) && DELTALESSTHAN(mode->height, RULE->resolution.y, 1) && + DELTALESSTHAN(mode->refresh / 1000.f, RULE->refreshRate, 1)) { + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f); continue; } - Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate, mode->width, mode->height, mode->refresh); + Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, mode->width, mode->height, mode->refresh); found = true; @@ -1795,83 +1826,83 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } if (!found) { - wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); - pMonitor->vecSize = pMonitorRule->resolution; - pMonitor->refreshRate = pMonitorRule->refreshRate; + wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)RULE->resolution.x, (int)RULE->resolution.y, WLRREFRESHRATE); + pMonitor->vecSize = RULE->resolution; + pMonitor->refreshRate = RULE->refreshRate; - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Custom resolution FAILED, falling back to preferred"); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); if (!PREFERREDMODE) { - Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate); + Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, RULE->resolution, + (float)RULE->refreshRate); return true; } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); - Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, - pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); + Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height); } else { - Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", pMonitorRule->resolution, (float)pMonitorRule->refreshRate); + Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate); } } } else { // custom resolution bool fail = false; - if (pMonitorRule->drmMode.type == DRM_MODE_TYPE_USERDEF) { + if (RULE->drmMode.type == DRM_MODE_TYPE_USERDEF) { if (!wlr_output_is_drm(pMonitor->output)) { Debug::log(ERR, "Tried to set custom modeline on non-DRM output"); fail = true; } else { - auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &pMonitorRule->drmMode); + auto* mode = wlr_drm_connector_add_mode(pMonitor->output, &RULE->drmMode); if (mode) { - wlr_output_set_mode(pMonitor->output, mode); - pMonitor->customDrmMode = pMonitorRule->drmMode; + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); + pMonitor->customDrmMode = RULE->drmMode; } else { Debug::log(ERR, "wlr_drm_connector_add_mode failed"); fail = true; } } } else { - wlr_output_set_custom_mode(pMonitor->output, (int)pMonitorRule->resolution.x, (int)pMonitorRule->resolution.y, WLRREFRESHRATE); + wlr_output_state_set_custom_mode(pMonitor->state.wlr(), (int)RULE->resolution.x, (int)RULE->resolution.y, WLRREFRESHRATE); } - pMonitor->vecSize = pMonitorRule->resolution; - pMonitor->refreshRate = pMonitorRule->refreshRate; + pMonitor->vecSize = RULE->resolution; + pMonitor->refreshRate = RULE->refreshRate; - if (fail || !wlr_output_test(pMonitor->output)) { + if (fail || !wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "Custom resolution FAILED, falling back to preferred"); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); if (!PREFERREDMODE) { - Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->output->name, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate); + Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate); return true; } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); - Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, - pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); + Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height); pMonitor->customDrmMode = {}; } else { - Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", pMonitorRule->resolution, (float)pMonitorRule->refreshRate); + Debug::log(LOG, "Set a custom mode {:X0}@{:2f} (mode not found in monitor modes)", RULE->resolution, (float)RULE->refreshRate); } } - } else if (pMonitorRule->resolution != Vector2D()) { + } else if (RULE->resolution != Vector2D()) { if (!wl_list_empty(&pMonitor->output->modes)) { wlr_output_mode* mode; float currentWidth = 0; @@ -1880,11 +1911,11 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR bool success = false; //(-1,-1) indicates a preference to refreshrate over resolution, (-1,-2) preference to resolution - if (pMonitorRule->resolution == Vector2D(-1, -1)) { + if (RULE->resolution == Vector2D(-1, -1)) { wl_list_for_each(mode, &pMonitor->output->modes, link) { if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || mode->refresh > (currentRefresh + 3000.f)) { - wlr_output_set_mode(pMonitor->output, mode); - if (wlr_output_test(pMonitor->output)) { + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); + if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { currentWidth = mode->width; currentHeight = mode->height; currentRefresh = mode->refresh; @@ -1896,8 +1927,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR wl_list_for_each(mode, &pMonitor->output->modes, link) { if ((mode->width >= currentWidth && mode->height >= currentHeight && mode->refresh >= (currentRefresh - 1000.f)) || (mode->width > currentWidth && mode->height > currentHeight)) { - wlr_output_set_mode(pMonitor->output, mode); - if (wlr_output_test(pMonitor->output)) { + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); + if (wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { currentWidth = mode->width; currentHeight = mode->height; currentRefresh = mode->refresh; @@ -1908,22 +1939,21 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } if (!success) { - Debug::log(LOG, "Monitor {}: REJECTED mode: {:X0}@{:2f}! Falling back to preferred: {}x{}@{:2f}", pMonitor->output->name, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate, mode->width, mode->height, mode->refresh / 1000.f); + Debug::log(LOG, "Monitor {}: REJECTED mode: {:X0}@{:2f}! Falling back to preferred: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, mode->width, mode->height, mode->refresh / 1000.f); const auto PREFERREDMODE = wlr_output_preferred_mode(pMonitor->output); if (!PREFERREDMODE) { - Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate); + Debug::log(ERR, "Monitor {} has NO PREFERRED MODE, and an INVALID one was requested: {:X0}@{:2f}", pMonitor->ID, RULE->resolution, (float)RULE->refreshRate); return true; } // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); - Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, - pMonitorRule->resolution, (float)pMonitorRule->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); + Debug::log(ERR, "Monitor {} got an invalid requested mode: {:X0}@{:2f}, using the preferred one instead: {}x{}@{:2f}", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, PREFERREDMODE->width, PREFERREDMODE->height, PREFERREDMODE->refresh / 1000.f); pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height); @@ -1945,15 +1975,15 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR wlr_output_mode* mode; wl_list_for_each(mode, &pMonitor->output->modes, link) { - wlr_output_set_mode(pMonitor->output, mode); + wlr_output_state_set_mode(pMonitor->state.wlr(), mode); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(LOG, "Monitor {}: REJECTED available mode: {}x{}@{:2f}!", pMonitor->output->name, mode->width, mode->height, mode->refresh / 1000.f); continue; } - Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, pMonitorRule->resolution, - (float)pMonitorRule->refreshRate, mode->width, mode->height, mode->refresh); + Debug::log(LOG, "Monitor {}: requested {:X0}@{:2f}, found available mode: {}x{}@{}mHz, applying.", pMonitor->output->name, RULE->resolution, + (float)RULE->refreshRate, mode->width, mode->height, mode->refresh); pMonitor->refreshRate = mode->refresh / 1000.f; pMonitor->vecSize = Vector2D(mode->width, mode->height); @@ -1963,7 +1993,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } } else { // Preferred is valid - wlr_output_set_mode(pMonitor->output, PREFERREDMODE); + wlr_output_state_set_mode(pMonitor->state.wlr(), PREFERREDMODE); pMonitor->vecSize = Vector2D(PREFERREDMODE->width, PREFERREDMODE->height); pMonitor->refreshRate = PREFERREDMODE->refresh / 1000.f; @@ -1972,12 +2002,13 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } } - pMonitor->vrrActive = pMonitor->output->pending.adaptive_sync_enabled; // disabled here, will be tested in CConfigManager::ensureVRR() + pMonitor->vrrActive = pMonitor->state.wlr()->adaptive_sync_enabled // disabled here, will be tested in CConfigManager::ensureVRR() + || pMonitor->createdByUser; // wayland backend doesn't allow for disabling adaptive_sync pMonitor->vecPixelSize = pMonitor->vecSize; Vector2D logicalSize = pMonitor->vecPixelSize / pMonitor->scale; - if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) { + if (!**PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) { // invalid scale, will produce fractional pixels. // find the nearest valid. @@ -1989,6 +2020,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR Vector2D logicalZero = pMonitor->vecPixelSize / scaleZero; if (logicalZero == logicalZero.round()) { pMonitor->scale = scaleZero; + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->scale); } else { for (size_t i = 1; i < 90; ++i) { double scaleUp = (searchScale + i) / 120.0; @@ -2026,11 +2058,15 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR } else pMonitor->scale = searchScale; } + + // for wlroots, that likes flooring, we have to do this. + double logicalX = std::round(pMonitor->vecPixelSize.x / pMonitor->scale); + logicalX += 0.1; + + wlr_output_state_set_scale(pMonitor->state.wlr(), pMonitor->vecPixelSize.x / logicalX); } } - wlr_output_set_scale(pMonitor->output, pMonitor->scale); - // clang-format off static const std::array>, 2> formats{ std::vector>{ /* 10-bit */ @@ -2045,14 +2081,14 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR bool set10bit = false; pMonitor->drmFormat = DRM_FORMAT_INVALID; - for (auto& fmt : formats[(int)!pMonitorRule->enable10bit]) { - wlr_output_set_render_format(pMonitor->output, fmt.second); + for (auto& fmt : formats[(int)!RULE->enable10bit]) { + wlr_output_state_set_render_format(pMonitor->state.wlr(), fmt.second); - if (!wlr_output_test(pMonitor->output)) { + if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) { Debug::log(ERR, "output {} failed basic test on format {}", pMonitor->szName, fmt.first); } else { Debug::log(LOG, "output {} succeeded basic test on format {}", pMonitor->szName, fmt.first); - if (pMonitorRule->enable10bit && fmt.first.contains("101010")) + if (RULE->enable10bit && fmt.first.contains("101010")) set10bit = true; pMonitor->drmFormat = fmt.second; @@ -2062,9 +2098,8 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->enabled10bit = set10bit; - if (!wlr_output_commit(pMonitor->output)) { + if (!pMonitor->state.commit()) Debug::log(ERR, "Couldn't commit output named {}", pMonitor->output->name); - } int x, y; wlr_output_transformed_resolution(pMonitor->output, &x, &y); @@ -2080,14 +2115,21 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->updateMatrix(); - // update renderer (here because it will call rollback, so we cannot do this before committing) - g_pHyprOpenGL->destroyMonitorResources(pMonitor); + if (WAS10B != pMonitor->enabled10bit || OLDRES != pMonitor->vecPixelSize) + g_pHyprOpenGL->destroyMonitorResources(pMonitor); // updato wlroots g_pCompositor->arrangeMonitors(); wlr_damage_ring_set_bounds(&pMonitor->damage, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y); + // Set scale for all surfaces on this monitor, needed for some clients + // but not on unsafe state to avoid crashes + if (!g_pCompositor->m_bUnsafeState) { + for (auto& w : g_pCompositor->m_vWindows) { + w->updateSurfaceScaleTransformDetails(); + } + } // updato us arrangeLayersForMonitor(pMonitor->ID); @@ -2110,7 +2152,7 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR void CHyprRenderer::setCursorSurface(wlr_surface* surf, int hotspotX, int hotspotY, bool force) { m_bCursorHasSurface = surf; - if ((surf == m_sLastCursorData.surf || m_bCursorHidden) && !force) + if (surf == m_sLastCursorData.surf && hotspotX == m_sLastCursorData.hotspotX && hotspotY == m_sLastCursorData.hotspotY && !force) return; m_sLastCursorData.name = ""; @@ -2118,29 +2160,35 @@ void CHyprRenderer::setCursorSurface(wlr_surface* surf, int hotspotX, int hotspo m_sLastCursorData.hotspotX = hotspotX; m_sLastCursorData.hotspotY = hotspotY; + if (m_bCursorHidden && !force) + return; + wlr_cursor_set_surface(g_pCompositor->m_sWLRCursor, surf, hotspotX, hotspotY); } void CHyprRenderer::setCursorFromName(const std::string& name, bool force) { m_bCursorHasSurface = true; - if ((name == m_sLastCursorData.name || m_bCursorHidden) && !force) + if (name == m_sLastCursorData.name && !force) return; m_sLastCursorData.name = name; m_sLastCursorData.surf.reset(); + if (m_bCursorHidden && !force) + return; + wlr_cursor_set_xcursor(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sWLRXCursorMgr, name.c_str()); } void CHyprRenderer::ensureCursorRenderingMode() { - static auto* const PCURSORTIMEOUT = &g_pConfigManager->getConfigValuePtr("general:cursor_inactive_timeout")->intValue; - static auto* const PHIDEONTOUCH = &g_pConfigManager->getConfigValuePtr("misc:hide_cursor_on_touch")->intValue; + static auto* const PCURSORTIMEOUT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:cursor_inactive_timeout"); + static auto* const PHIDEONTOUCH = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("misc:hide_cursor_on_touch"); const auto PASSEDCURSORSECONDS = g_pInputManager->m_tmrLastCursorMovement.getSeconds(); - if (*PCURSORTIMEOUT > 0 || *PHIDEONTOUCH) { - const bool HIDE = (*PCURSORTIMEOUT > 0 && *PCURSORTIMEOUT < PASSEDCURSORSECONDS) || (g_pInputManager->m_bLastInputTouch && *PHIDEONTOUCH); + if (**PCURSORTIMEOUT > 0 || **PHIDEONTOUCH) { + const bool HIDE = (**PCURSORTIMEOUT > 0 && **PCURSORTIMEOUT < PASSEDCURSORSECONDS) || (g_pInputManager->m_bLastInputTouch && **PHIDEONTOUCH); if (HIDE && !m_bCursorHidden) { Debug::log(LOG, "Hiding the cursor (timeout)"); @@ -2231,7 +2279,9 @@ void CHyprRenderer::initiateManualCrash() { g_pHyprOpenGL->m_tGlobalTimer.reset(); - g_pConfigManager->setInt("debug:damage_tracking", 0); + static auto* const PDT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("debug:damage_tracking"); + + **PDT = 0; } void CHyprRenderer::setOccludedForMainWorkspace(CRegion& region, CWorkspace* pWorkspace) { @@ -2318,6 +2368,9 @@ bool CHyprRenderer::canSkipBackBufferClear(CMonitor* pMonitor) { void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { pMonitor->solitaryClient = nullptr; // reset it, if we find one it will be set. + if (g_pHyprNotificationOverlay->hasAny()) + return; + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pMonitor->activeWorkspace); if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || g_pInputManager->m_sDrag.drag || g_pCompositor->m_sSeat.exclusiveClient || pMonitor->specialWorkspaceID || @@ -2345,7 +2398,8 @@ void CHyprRenderer::recheckSolitaryForMonitor(CMonitor* pMonitor) { } for (auto& w : g_pCompositor->m_vWindows) { - if (w->m_iWorkspaceID == PCANDIDATE->m_iWorkspaceID && w->m_bIsFloating && w->m_bCreatedOverFullscreen && !w->isHidden() && w->m_bIsMapped && w.get() != PCANDIDATE) + if (w->m_iWorkspaceID == PCANDIDATE->m_iWorkspaceID && w->m_bIsFloating && w->m_bCreatedOverFullscreen && !w->isHidden() && (w->m_bIsMapped || w->m_bFadingOut) && + w.get() != PCANDIDATE) return; } @@ -2417,37 +2471,44 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode if (mode == RENDER_MODE_FULL_FAKE) { RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!"); fb->bind(); - g_pHyprOpenGL->begin(pMonitor, &damage, fb); + g_pHyprOpenGL->begin(pMonitor, damage, fb); return true; } if (!buffer) { - if (!wlr_output_configure_primary_swapchain(pMonitor->output, &pMonitor->output->pending, &pMonitor->output->swapchain)) + if (!wlr_output_configure_primary_swapchain(pMonitor->output, pMonitor->state.wlr(), &pMonitor->output->swapchain)) { + Debug::log(ERR, "Failed to configure primary swapchain for {}", pMonitor->szName); return false; + } - m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, &m_iLastBufferAge); - if (!m_pCurrentWlrBuffer) + m_pCurrentWlrBuffer = wlr_swapchain_acquire(pMonitor->output->swapchain, nullptr); + if (!m_pCurrentWlrBuffer) { + Debug::log(ERR, "Failed to acquire swapchain buffer for {}", pMonitor->szName); return false; - } else { + } + } else m_pCurrentWlrBuffer = wlr_buffer_lock(buffer); - } try { m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentWlrBuffer, pMonitor->drmFormat); } catch (std::exception& e) { + Debug::log(ERR, "getOrCreateRenderbuffer failed for {}", pMonitor->szName); wlr_buffer_unlock(m_pCurrentWlrBuffer); return false; } + if (mode == RENDER_MODE_NORMAL) + wlr_damage_ring_rotate_buffer(&pMonitor->damage, m_pCurrentWlrBuffer, damage.pixman()); + m_pCurrentRenderbuffer->bind(); - g_pHyprOpenGL->begin(pMonitor, &damage); + g_pHyprOpenGL->begin(pMonitor, damage); return true; } void CHyprRenderer::endRender() { const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor; - static auto* const PNVIDIAANTIFLICKER = &g_pConfigManager->getConfigValuePtr("opengl:nvidia_anti_flicker")->intValue; + static auto* const PNVIDIAANTIFLICKER = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("opengl:nvidia_anti_flicker"); if (m_eRenderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) g_pHyprOpenGL->end(); @@ -2460,13 +2521,13 @@ void CHyprRenderer::endRender() { if (m_eRenderMode == RENDER_MODE_FULL_FAKE) return; - if (isNvidia() && *PNVIDIAANTIFLICKER) + if (isNvidia() && **PNVIDIAANTIFLICKER) glFinish(); else glFlush(); if (m_eRenderMode == RENDER_MODE_NORMAL) { - wlr_output_state_set_buffer(&PMONITOR->output->pending, m_pCurrentWlrBuffer); + wlr_output_state_set_buffer(PMONITOR->state.wlr(), m_pCurrentWlrBuffer); unsetEGL(); // flush the context } @@ -2476,7 +2537,6 @@ void CHyprRenderer::endRender() { m_pCurrentRenderbuffer = nullptr; m_pCurrentWlrBuffer = nullptr; - m_iLastBufferAge = 0; } void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) { diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index bc914558..5eafb639 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -57,7 +57,7 @@ class CHyprRenderer { void ensureCursorRenderingMode(); bool shouldRenderCursor(); void setCursorHidden(bool hide); - void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false); + void calculateUVForSurface(CWindow*, wlr_surface*, bool main = false, const Vector2D& projSize = {}, bool fixMisalignedFSV1 = false); std::tuple getRenderTimes(CMonitor* pMonitor); // avg max min void renderLockscreen(CMonitor* pMonitor, timespec* now); void setOccludedForBackLayers(CRegion& region, CWorkspace* pWorkspace); @@ -73,6 +73,8 @@ class CHyprRenderer { void makeEGLCurrent(); void unsetEGL(); + // if RENDER_MODE_NORMAL, provided damage will be written to. + // otherwise, it will be the one used. bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, wlr_buffer* buffer = nullptr, CFramebuffer* fb = nullptr); void endRender(); @@ -82,7 +84,6 @@ class CHyprRenderer { CMonitor* m_pMostHzMonitor = nullptr; bool m_bDirectScanoutBlocked = false; bool m_bSoftwareCursorsLocked = false; - bool m_bTearingEnvSatisfied = false; DAMAGETRACKINGMODES damageTrackingModeFromStr(const std::string&); @@ -123,7 +124,6 @@ class CHyprRenderer { CRenderbuffer* m_pCurrentRenderbuffer = nullptr; wlr_buffer* m_pCurrentWlrBuffer = nullptr; eRenderMode m_eRenderMode = RENDER_MODE_NORMAL; - int m_iLastBufferAge = 0; bool m_bNvidia = false; diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index 70fe468c..185c3dff 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -42,9 +42,9 @@ class CShader { GLint gradientLength = -1; GLint angle = -1; - GLint time = -1; - GLint distort = -1; - GLint output = -1; + GLint time = -1; + GLint distort = -1; + GLint wl_output = -1; // Blur prepare GLint contrast = -1; diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index 4e3e824e..1ed6c144 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -94,9 +94,9 @@ eDecorationLayer CHyprBorderDecoration::getDecorationLayer() { } uint64_t CHyprBorderDecoration::getDecorationFlags() { - static auto* const PPARTOFWINDOW = &g_pConfigManager->getConfigValuePtr("general:border_part_of_window")->intValue; + static auto* const PPARTOFWINDOW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("general:border_part_of_window"); - return *PPARTOFWINDOW && !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0; + return **PPARTOFWINDOW && !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0; } std::string CHyprBorderDecoration::getDisplayName() { @@ -104,5 +104,5 @@ std::string CHyprBorderDecoration::getDisplayName() { } bool CHyprBorderDecoration::doesntWantBorders() { - return !m_pWindow->m_sSpecialRenderData.border || m_pWindow->m_bX11DoesntWantBorders; + return !m_pWindow->m_sSpecialRenderData.border || m_pWindow->m_bX11DoesntWantBorders || m_pWindow->getRealBorderSize() == 0; } diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 0e5e9d94..4448ce9d 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.cpp +++ b/src/render/decorations/CHyprDropShadowDecoration.cpp @@ -35,9 +35,9 @@ std::string CHyprDropShadowDecoration::getDisplayName() { } void CHyprDropShadowDecoration::damageEntire() { - static auto* const PSHADOWS = &g_pConfigManager->getConfigValuePtr("decoration:drop_shadow")->intValue; + static auto* const PSHADOWS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:drop_shadow"); - if (*PSHADOWS != 1) + if (**PSHADOWS != 1) return; // disabled CBox dm = {m_vLastWindowPos.x - m_seExtents.topLeft.x, m_vLastWindowPos.y - m_seExtents.topLeft.y, m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x, @@ -70,13 +70,13 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D if (m_pWindow->m_sAdditionalConfigData.forceNoShadow) return; - static auto* const PSHADOWS = &g_pConfigManager->getConfigValuePtr("decoration:drop_shadow")->intValue; - static auto* const PSHADOWSIZE = &g_pConfigManager->getConfigValuePtr("decoration:shadow_range")->intValue; - static auto* const PSHADOWIGNOREWINDOW = &g_pConfigManager->getConfigValuePtr("decoration:shadow_ignore_window")->intValue; - static auto* const PSHADOWSCALE = &g_pConfigManager->getConfigValuePtr("decoration:shadow_scale")->floatValue; - static auto* const PSHADOWOFFSET = &g_pConfigManager->getConfigValuePtr("decoration:shadow_offset")->vecValue; + static auto* const PSHADOWS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:drop_shadow"); + static auto* const PSHADOWSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:shadow_range"); + static auto* const PSHADOWIGNOREWINDOW = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("decoration:shadow_ignore_window"); + static auto* const PSHADOWSCALE = (Hyprlang::FLOAT* const*)g_pConfigManager->getConfigValuePtr("decoration:shadow_scale"); + static auto* const PSHADOWOFFSET = (Hyprlang::VEC2* const*)g_pConfigManager->getConfigValuePtr("decoration:shadow_offset"); - if (*PSHADOWS != 1) + if (**PSHADOWS != 1) return; // disabled const auto ROUNDINGBASE = m_pWindow->rounding(); @@ -87,15 +87,15 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D // draw the shadow CBox fullBox = m_bLastWindowBoxWithDecos; fullBox.translate(-pMonitor->vecPosition + WORKSPACEOFFSET); - fullBox.x -= *PSHADOWSIZE; - fullBox.y -= *PSHADOWSIZE; - fullBox.w += 2 * *PSHADOWSIZE; - fullBox.h += 2 * *PSHADOWSIZE; + fullBox.x -= **PSHADOWSIZE; + fullBox.y -= **PSHADOWSIZE; + fullBox.w += 2 * **PSHADOWSIZE; + fullBox.h += 2 * **PSHADOWSIZE; - const float SHADOWSCALE = std::clamp(*PSHADOWSCALE, 0.f, 1.f); + const float SHADOWSCALE = std::clamp(**PSHADOWSCALE, 0.f, 1.f); // scale the box in relation to the center of the box - fullBox.scaleFromCenter(SHADOWSCALE).translate(*PSHADOWOFFSET); + fullBox.scaleFromCenter(SHADOWSCALE).translate(**PSHADOWOFFSET); m_vLastWindowPos += WORKSPACEOFFSET; m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2}, @@ -116,7 +116,7 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D fullBox.scale(pMonitor->scale).round(); - if (*PSHADOWIGNOREWINDOW) { + if (**PSHADOWIGNOREWINDOW) { CBox windowBox = m_bLastWindowBox; CBox withDecos = m_bLastWindowBoxWithDecos; @@ -147,7 +147,7 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D g_pHyprOpenGL->renderRect(&fullBox, CColor(0, 0, 0, 1), 0); // render white shadow with the alpha of the shadow color (otherwise we clear with alpha later and shit it to 2 bit) - g_pHyprOpenGL->renderRoundedShadow(&fullBox, ROUNDING * pMonitor->scale, *PSHADOWSIZE * pMonitor->scale, CColor(1, 1, 1, m_pWindow->m_cRealShadowColor.col().a), a); + g_pHyprOpenGL->renderRoundedShadow(&fullBox, ROUNDING * pMonitor->scale, **PSHADOWSIZE * pMonitor->scale, CColor(1, 1, 1, m_pWindow->m_cRealShadowColor.col().a), a); // render black window box ("clip") g_pHyprOpenGL->renderRect(&windowBox, CColor(0, 0, 0, 1.0), ROUNDING * pMonitor->scale); @@ -166,7 +166,7 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D g_pHyprOpenGL->m_RenderData.damage = saveDamage; } else { - g_pHyprOpenGL->renderRoundedShadow(&fullBox, ROUNDING * pMonitor->scale, *PSHADOWSIZE * pMonitor->scale, m_pWindow->m_cRealShadowColor.col(), a); + g_pHyprOpenGL->renderRoundedShadow(&fullBox, ROUNDING * pMonitor->scale, **PSHADOWSIZE * pMonitor->scale, m_pWindow->m_cRealShadowColor.col(), a); } if (m_seExtents != m_seReportedExtents) diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 33ecff6a..20c0240f 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -15,29 +15,31 @@ constexpr int BAR_TEXT_PAD = 2; constexpr int BAR_HORIZONTAL_PADDING = 2; CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) : IHyprWindowDecoration(pWindow) { - static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; + static auto* const PGRADIENTS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:enabled"); + static auto* const PENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:gradients"); m_pWindow = pWindow; - if (m_tGradientActive.m_iTexID == 0 && *PENABLED && *PGRADIENTS) + if (m_tGradientActive.m_iTexID == 0 && **PENABLED && **PGRADIENTS) refreshGroupBarGradients(); } CHyprGroupBarDecoration::~CHyprGroupBarDecoration() {} SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { - static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; - static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; + static auto* const PHEIGHT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:height"); + static auto* const PENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:enabled"); + static auto* const PRENDERTITLES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles"); + static auto* const PGRADIENTS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:gradients"); + static auto* const PPRIORITY = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:priority"); SDecorationPositioningInfo info; info.policy = DECORATION_POSITION_STICKY; info.edges = DECORATION_EDGE_TOP; - info.priority = g_pConfigManager->getConfigValuePtr("group:groupbar:priority")->intValue; + info.priority = **PPRIORITY; info.reserved = true; - if (*PENABLED && m_pWindow->m_sSpecialRenderData.decorate) - info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2}, {0, 0}}; + if (**PENABLED && m_pWindow->m_sSpecialRenderData.decorate) + info.desiredExtents = {{0, BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (**PGRADIENTS || **PRENDERTITLES ? **PHEIGHT : 0) + 2}, {0, 0}}; else info.desiredExtents = {{0, 0}, {0, 0}}; @@ -87,19 +89,20 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& // get how many bars we will draw int barsToDraw = m_dwGroupMembers.size(); - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; - static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue; - static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; - static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; + static auto* const PENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:enabled"); + static auto* const PRENDERTITLES = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles"); + static auto* const PTITLEFONTSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:font_size"); + static auto* const PHEIGHT = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:height"); + static auto* const PGRADIENTS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:gradients"); - if (!*PENABLED || !m_pWindow->m_sSpecialRenderData.decorate) + if (!**PENABLED || !m_pWindow->m_sSpecialRenderData.decorate) return; const auto ASSIGNEDBOX = assignedBoxGlobal(); m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw; - const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2; + const auto DESIREDHEIGHT = BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (**PGRADIENTS || **PRENDERTITLES ? **PHEIGHT : 0) + 2; if (DESIREDHEIGHT != ASSIGNEDBOX.h) g_pDecorationPositioner->repositionDeco(this); @@ -114,42 +117,45 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& rect.scale(pMonitor->scale); - static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data; - static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data; - static auto* const PGROUPCOLACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data; - static auto* const PGROUPCOLINACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data; + static auto* const PGROUPCOLACTIVE = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.active"); + static auto* const PGROUPCOLINACTIVE = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive"); + static auto* const PGROUPCOLACTIVELOCKED = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active"); + static auto* const PGROUPCOLINACTIVELOCKED = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive"); + auto* const GROUPCOLACTIVE = (CGradientValueData*)(*PGROUPCOLACTIVE)->getData(); + auto* const GROUPCOLINACTIVE = (CGradientValueData*)(*PGROUPCOLINACTIVE)->getData(); + auto* const GROUPCOLACTIVELOCKED = (CGradientValueData*)(*PGROUPCOLACTIVELOCKED)->getData(); + auto* const GROUPCOLINACTIVELOCKED = (CGradientValueData*)(*PGROUPCOLINACTIVELOCKED)->getData(); const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked; - const auto* const PCOLACTIVE = GROUPLOCKED ? PGROUPCOLACTIVELOCKED : PGROUPCOLACTIVE; - const auto* const PCOLINACTIVE = GROUPLOCKED ? PGROUPCOLINACTIVELOCKED : PGROUPCOLINACTIVE; + const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE; + const auto* const PCOLINACTIVE = GROUPLOCKED ? GROUPCOLINACTIVELOCKED : GROUPCOLINACTIVE; - CColor color = - m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? ((CGradientValueData*)PCOLACTIVE->get())->m_vColors[0] : ((CGradientValueData*)PCOLINACTIVE->get())->m_vColors[0]; + CColor color = m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0]; color.a *= a; g_pHyprOpenGL->renderRect(&rect, color); - // render title if necessary - if (*PRENDERTITLES) { - CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, - ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2}; - rect.scale(pMonitor->scale); + rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth, + ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2}; + rect.scale(pMonitor->scale); + if (*PGRADIENTS) { + const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : + (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); + if (GRADIENTTEX.m_iTexID != 0) + g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); + } + + if (**PRENDERTITLES) { CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle); if (!pTitleTex) pTitleTex = m_sTitleTexs.titleTexs .emplace_back(std::make_unique(m_dwGroupMembers[i], - Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale})) + Vector2D{m_fBarWidth * pMonitor->scale, (**PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale})) .get(); - const auto& GRADIENTTEX = (m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : - (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); - - if (*PGRADIENTS && GRADIENTTEX.m_iTexID != 0) - g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); - - rect.y += (ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; - rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; + rect.y += (ASSIGNEDBOX.h / 2.0 - (**PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; + rect.height = (**PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f); } @@ -157,7 +163,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& xoff += BAR_HORIZONTAL_PADDING + m_fBarWidth; } - if (*PRENDERTITLES) + if (**PRENDERTITLES) invalidateTextures(); } @@ -179,12 +185,13 @@ CTitleTex::CTitleTex(CWindow* pWindow, const Vector2D& bufferSize) { pWindowOwner = pWindow; const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); + const auto MONITORSCALE = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID)->scale; - static auto* const PTITLEFONTFAMILY = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_family")->strValue; - static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue; - static auto* const PTEXTCOLOR = &g_pConfigManager->getConfigValuePtr("group:groupbar:text_color")->intValue; + static auto* const PTITLEFONTFAMILY = (Hyprlang::STRING const*)g_pConfigManager->getConfigValuePtr("group:groupbar:font_family"); + static auto* const PTITLEFONTSIZE = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:font_size"); + static auto* const PTEXTCOLOR = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:text_color"); - const CColor COLOR = CColor(*PTEXTCOLOR); + const CColor COLOR = CColor(**PTEXTCOLOR); // clear the pixmap cairo_save(CAIRO); @@ -196,8 +203,8 @@ CTitleTex::CTitleTex(CWindow* pWindow, const Vector2D& bufferSize) { PangoLayout* layout = pango_cairo_create_layout(CAIRO); pango_layout_set_text(layout, szContent.c_str(), -1); - PangoFontDescription* fontDesc = pango_font_description_from_string(PTITLEFONTFAMILY->c_str()); - pango_font_description_set_size(fontDesc, *PTITLEFONTSIZE * PANGO_SCALE); + PangoFontDescription* fontDesc = pango_font_description_from_string(*PTITLEFONTFAMILY); + pango_font_description_set_size(fontDesc, **PTITLEFONTSIZE * PANGO_SCALE); pango_layout_set_font_description(layout, fontDesc); pango_font_description_free(fontDesc); @@ -243,7 +250,7 @@ CTitleTex::~CTitleTex() { tex.destroyTexture(); } -void renderGradientTo(CTexture& tex, const CColor& grad) { +void renderGradientTo(CTexture& tex, CGradientValueData* grad) { if (!g_pCompositor->m_pLastMonitor) return; @@ -261,8 +268,12 @@ void renderGradientTo(CTexture& tex, const CColor& grad) { cairo_pattern_t* pattern; pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y); - cairo_pattern_add_color_stop_rgba(pattern, 1, grad.r, grad.g, grad.b, grad.a); - cairo_pattern_add_color_stop_rgba(pattern, 0, grad.r, grad.g, grad.b, 0); + + for (unsigned long i = 0; i < grad->m_vColors.size(); i++) { + cairo_pattern_add_color_stop_rgba(pattern, 1 - (double)(i + 1) / (grad->m_vColors.size() + 1), grad->m_vColors[i].r, grad->m_vColors[i].g, grad->m_vColors[i].b, + grad->m_vColors[i].a); + } + cairo_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y); cairo_set_source(CAIRO, pattern); cairo_fill(CAIRO); @@ -290,13 +301,17 @@ void renderGradientTo(CTexture& tex, const CColor& grad) { } void refreshGroupBarGradients() { - static auto* const PGRADIENTS = &g_pConfigManager->getConfigValuePtr("group:groupbar:enabled")->intValue; - static auto* const PENABLED = &g_pConfigManager->getConfigValuePtr("group:groupbar:gradients")->intValue; + static auto* const PGRADIENTS = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:enabled"); + static auto* const PENABLED = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:gradients"); - static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.active")->data; - static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive")->data; - static auto* const PGROUPCOLACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active")->data; - static auto* const PGROUPCOLINACTIVELOCKED = &g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive")->data; + static auto* const PGROUPCOLACTIVE = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.active"); + static auto* const PGROUPCOLINACTIVE = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.inactive"); + static auto* const PGROUPCOLACTIVELOCKED = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_active"); + static auto* const PGROUPCOLINACTIVELOCKED = (Hyprlang::CUSTOMTYPE* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:col.locked_inactive"); + auto* const GROUPCOLACTIVE = (CGradientValueData*)(*PGROUPCOLACTIVE)->getData(); + auto* const GROUPCOLINACTIVE = (CGradientValueData*)(*PGROUPCOLINACTIVE)->getData(); + auto* const GROUPCOLACTIVELOCKED = (CGradientValueData*)(*PGROUPCOLACTIVELOCKED)->getData(); + auto* const GROUPCOLINACTIVELOCKED = (CGradientValueData*)(*PGROUPCOLINACTIVELOCKED)->getData(); g_pHyprRenderer->makeEGLCurrent(); @@ -307,16 +322,19 @@ void refreshGroupBarGradients() { m_tGradientLockedInactive.destroyTexture(); } - if (!*PENABLED || !*PGRADIENTS) + if (!**PENABLED || !**PGRADIENTS) return; - renderGradientTo(m_tGradientActive, ((CGradientValueData*)PGROUPCOLACTIVE->get())->m_vColors[0]); - renderGradientTo(m_tGradientInactive, ((CGradientValueData*)PGROUPCOLINACTIVE->get())->m_vColors[0]); - renderGradientTo(m_tGradientLockedActive, ((CGradientValueData*)PGROUPCOLACTIVELOCKED->get())->m_vColors[0]); - renderGradientTo(m_tGradientLockedInactive, ((CGradientValueData*)PGROUPCOLINACTIVELOCKED->get())->m_vColors[0]); + renderGradientTo(m_tGradientActive, GROUPCOLACTIVE); + renderGradientTo(m_tGradientInactive, GROUPCOLINACTIVE); + renderGradientTo(m_tGradientLockedActive, GROUPCOLACTIVELOCKED); + renderGradientTo(m_tGradientLockedInactive, GROUPCOLINACTIVELOCKED); } bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { + if (m_pWindow == m_pWindow->m_sGroupData.pNextWindow) + return false; + const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; const int WINDOWINDEX = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING); @@ -442,11 +460,10 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, wlr_point } bool CHyprGroupBarDecoration::onScrollOnDeco(const Vector2D& pos, wlr_pointer_axis_event* e) { - static auto* const PGROUPBARSCROLLING = &g_pConfigManager->getConfigValuePtr("group:groupbar:scrolling")->intValue; + static auto* const PGROUPBARSCROLLING = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("group:groupbar:scrolling"); - if (!*PGROUPBARSCROLLING || !m_pWindow->m_sGroupData.pNextWindow) { + if (!**PGROUPBARSCROLLING || !m_pWindow->m_sGroupData.pNextWindow) return false; - } if (e->delta > 0) m_pWindow->setGroupCurrent(m_pWindow->m_sGroupData.pNextWindow); diff --git a/src/render/shaders/Border.hpp b/src/render/shaders/Border.hpp index 79493da9..a33694a7 100644 --- a/src/render/shaders/Border.hpp +++ b/src/render/shaders/Border.hpp @@ -4,7 +4,7 @@ // makes a stencil without corners inline const std::string FRAGBORDER1 = R"#( -precision mediump float; +precision highp float; varying vec4 v_color; varying vec2 v_texcoord; diff --git a/src/render/shaders/Shadow.hpp b/src/render/shaders/Shadow.hpp index 2c7b07b2..0acab6b7 100644 --- a/src/render/shaders/Shadow.hpp +++ b/src/render/shaders/Shadow.hpp @@ -3,7 +3,7 @@ #include inline const std::string FRAGSHADOW = R"#( -precision mediump float; +precision highp float; varying vec4 v_color; varying vec2 v_texcoord; diff --git a/src/render/shaders/Textures.hpp b/src/render/shaders/Textures.hpp index e78f39fc..5e346c1e 100644 --- a/src/render/shaders/Textures.hpp +++ b/src/render/shaders/Textures.hpp @@ -50,7 +50,7 @@ void main() { })#"; inline const std::string QUADFRAGSRC = R"#( -precision mediump float; +precision highp float; varying vec4 v_color; uniform vec2 topLeft; @@ -81,7 +81,7 @@ void main() { })#"; inline const std::string TEXFRAGSRCRGBA = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; uniform float alpha; @@ -122,7 +122,7 @@ void main() { })#"; inline const std::string TEXFRAGSRCRGBAPASSTHRU = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; @@ -131,7 +131,7 @@ void main() { })#"; inline const std::string TEXFRAGSRCRGBAMATTE = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; uniform sampler2D texMatte; @@ -141,7 +141,7 @@ void main() { })#"; inline const std::string TEXFRAGSRCRGBX = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; uniform sampler2D tex; uniform float alpha; @@ -180,8 +180,8 @@ void main() { inline const std::string FRAGBLUR1 = R"#( #version 100 -precision mediump float; -varying mediump vec2 v_texcoord; // is in 0-1 +precision highp float; +varying highp vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; uniform float radius; @@ -324,8 +324,8 @@ void main() { inline const std::string FRAGBLUR2 = R"#( #version 100 -precision mediump float; -varying mediump vec2 v_texcoord; // is in 0-1 +precision highp float; +varying highp vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; uniform float radius; @@ -349,7 +349,7 @@ void main() { )#"; inline const std::string FRAGBLURPREPARE = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; @@ -381,7 +381,7 @@ void main() { )#"; inline const std::string FRAGBLURFINISH = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; @@ -412,7 +412,7 @@ void main() { inline const std::string TEXFRAGSRCEXT = R"#( #extension GL_OES_EGL_image_external : require -precision mediump float; +precision highp float; varying vec2 v_texcoord; uniform samplerExternalOES texture0; uniform float alpha; @@ -451,7 +451,7 @@ void main() { )#"; static const std::string FRAGGLITCH = R"#( -precision mediump float; +precision highp float; varying vec2 v_texcoord; uniform sampler2D tex; uniform float time; // quirk: time is set to 0 at the beginning, should be around 10 when crash. diff --git a/subprojects/wlroots b/subprojects/wlroots index 153291f4..ac846178 160000 --- a/subprojects/wlroots +++ b/subprojects/wlroots @@ -1 +1 @@ -Subproject commit 153291f4c58eb2735e6337b50d7aa6e6accac099 +Subproject commit ac8461785665285bfc5cb00a0c3bf3bbce665a90