diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8adef5c4..77c9c48b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,34 +13,44 @@ jobs: 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 - - name: Set up user run: | useradd -m githubuser echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers - - name: Build wlroots run: | su githubuser -c "cd ~ && git clone https://gitlab.freedesktop.org/wlroots/wlroots" su githubuser -c "cd ~/wlroots && meson build/ --prefix=/usr && ninja -C build/ && sudo ninja -C build/ install && cd .." - - name: Fix permissions for git run: | git config --global --add safe.directory /__w/Hyprland/Hyprland - - name: Checkout Hyprland uses: actions/checkout@v3 with: submodules: recursive - - - name: Build Hyprland With default settings + - name: Build Hyprland run: | git submodule sync --recursive && git submodule update --init --force --recursive make all - - - name: Build Hyprland with LEGACY_RENDERER + - name: Compress and package artifacts run: | - make legacyrenderer + mkdir x86_64-pc-linux-gnu + mkdir hyprland + mkdir hyprland/example + mkdir hyprland/assets + cp ./LICENSE hyprland/ + cp build/Hyprland hyprland/ + cp hyprctl/hyprctl hyprland/ + cp subprojects/wlroots/build/libwlroots.so.11032 hyprland/ + cp build/Hyprland hyprland/ + cp -r example/ hyprland/ + cp -r assets/ hyprland/ + tar -cvf Hyprland.tar.xz hyprland + - name: Release + uses: actions/upload-artifact@v3 + with: + name: Build archive + path: Hyprland.tar.xz meson: name: "Build Hyprland with Meson (Arch)" @@ -63,13 +73,13 @@ jobs: -Ddefault_library=static - name: Compile run: ninja -C obj-x86_64-pc-linux-gnu - - name: Compress artifacts - run: | - mkdir x86_64-pc-linux-gnu - DESTDIR=$PWD/x86_64-pc-linux-gnu meson install -C obj-x86_64-pc-linux-gnu --tags runtime - tar -cvf x86_64-pc-linux-gnu.tar.xz x86_64-pc-linux-gnu - - name: Upload artifacts - uses: actions/upload-artifact@v3 - with: - name: Build artifacts (x86_64-pc-linux-gnu) - path: x86_64-pc-linux-gnu.tar.xz +# - name: Compress artifacts +# run: | +# mkdir x86_64-pc-linux-gnu +# DESTDIR=$PWD/x86_64-pc-linux-gnu meson install -C obj-x86_64-pc-linux-gnu --tags runtime +# tar -cvf x86_64-pc-linux-gnu.tar.xz x86_64-pc-linux-gnu +# - name: Upload artifacts +# uses: actions/upload-artifact@v3 +# with: +# name: Build artifacts (x86_64-pc-linux-gnu) +# path: x86_64-pc-linux-gnu.tar.xz diff --git a/.github/workflows/version-update.sh b/.github/workflows/version-update.sh new file mode 100644 index 00000000..1bf95af9 --- /dev/null +++ b/.github/workflows/version-update.sh @@ -0,0 +1,26 @@ +name: "Nix & Meson: update version" + +on: [push, workflow_dispatch] + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Update flake and meson version + run: | + REGEX="([0-9]+(\.[0-9a-zA-Z]+)+)" + + CRT_REV=$(git show-ref --tags --head --abbrev | head -n 1 | head -c 7) + TAG_REV=$(git show-ref --tags --abbrev | tail -n 1 | head -c 7) + CRT_VER=$(sed -nEe "/$REGEX/{p;q;}" meson.build | awk -F\' '{print $2}') + VERSION=$(git show-ref --tags --abbrev | tail -n 1 | tail -c +20) + + if [[ $TAG_REV = $CRT_REV ]] || [[ $CRT_VER != $VERSION ]]; then + sed -Ei "s/$REGEX/$VERSION/g" meson.build + sed -Ei "s/$REGEX/$VERSION/g" flake.nix + fi + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "[gha] bump flake and meson version" diff --git a/.gitmodules b/.gitmodules index 01c7b0d5..61cb4cef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "wlroots"] path = subprojects/wlroots - url = https://github.com/ThatOneCalculator/wlroots-mirror + url = https://gitlab.freedesktop.org/wlroots/wlroots.git diff --git a/CMakeLists.txt b/CMakeLists.txt index e78eea0e..44246e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ ENDIF(NO_XWAYLAND MATCHES true) IF(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring Hyprland in Debug with CMake!") + add_definitions( -DHYPRLAND_DEBUG ) ELSE() # add_compile_options(-O3) # may crash for some message(STATUS "Configuring Hyprland in Release with CMake!") diff --git a/Makefile b/Makefile index eb306932..bb753451 100644 --- a/Makefile +++ b/Makefile @@ -136,4 +136,4 @@ config: cd subprojects/wlroots && meson ./build --prefix=/usr --buildtype=release cd subprojects/wlroots && ninja -C build/ - cd subprojects/wlroots && sudo ninja -C build/ install + cd subprojects/wlroots && ninja -C build/ install diff --git a/README.md b/README.md index 68ff18d5..faf8acca 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-![Banner] +banner
@@ -151,7 +151,6 @@ Try it out and report bugs / suggestions! [Preview A]: https://i.imgur.com/NbrTnZH.png [Preview B]: https://i.imgur.com/ZA4Fa8R.png [Preview C]: https://i.imgur.com/BpXxM8H.png -[Banner]: https://raw.githubusercontent.com/vaxerski/Hyprland/main/assets/hyprland.png diff --git a/assets/header.svg b/assets/header.svg new file mode 100644 index 00000000..922313be --- /dev/null +++ b/assets/header.svg @@ -0,0 +1,83 @@ + + +
+ + + +

Hyprland

+

ハイパーランド

+
+
+
+
diff --git a/aur/PKGBUILD b/aur/PKGBUILD deleted file mode 100644 index deab5a93..00000000 --- a/aur/PKGBUILD +++ /dev/null @@ -1,72 +0,0 @@ -# Maintainer: ThatOneCalculator - -_pkgname="hyprland" -pkgname="${_pkgname}" -pkgver="0.4.0beta" -pkgrel=2 -pkgdesc="A dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks." -arch=(any) -url="https://github.com/vaxerski/Hyprland" -license=('BSD') -depends=( - libxcb - xcb-proto - xcb-util - xcb-util-keysyms - libxfixes - libx11 - libxcomposite - xorg-xinput - libxrender - pixman - wayland-protocols - cairo - pango - polkit - glslang - libinput - libxcb - libxkbcommon - opengl-driver - pixman - wayland - xcb-util-errors - xcb-util-renderutil - xcb-util-wm - seatd - vulkan-icd-loader - vulkan-validation-layers - xorg-xwayland) -makedepends=( - git - cmake - ninja - gcc - gdb - meson - vulkan-headers - wayland-protocols - xorgproto) -source=("${pkgname}-${pkgver}.tar.gz::https://github.com/vaxerski/hyprland/archive/v${pkgver}.tar.gz") -sha256sums=('5969e5f88426f90acdfb5958644733d8a9409389c2d345514c58a66cf74d2f91') -conflicts=("${_pkgname}") -provides=(hyprland) -options=(!makeflags !buildflags) - -build() { - cd "$srcdir/Hyprland-$pkgver" - git submodule update --init - make all -} - -package() { - cd "$srcdir/Hyprland-$pkgver" - mkdir -p "${pkgdir}/usr/share/wayland-sessions" - mkdir -p "${pkgdir}/usr/share/hyprland" - install -Dm755 build/Hyprland -t "${pkgdir}/usr/bin" - install -Dm755 hyprctl/hyprctl -t "${pkgdir}/usr/bin" - install -Dm644 assets/*.png -t "${pkgdir}/usr/share/hyprland" - install -Dm644 example/hyprland.desktop -t "${pkgdir}/usr/share/wayland-sessions" - install -Dm644 example/hyprland.conf -t "${pkgdir}/usr/share/hyprland" - install -Dm644 LICENSE -t "${pkgdir}/usr/share/licenses/${_pkgname}" -} diff --git a/aur/PKGBUILD-bin b/aur/PKGBUILD-bin deleted file mode 100644 index 62c3a9fa..00000000 --- a/aur/PKGBUILD-bin +++ /dev/null @@ -1,56 +0,0 @@ -# Maintainer: ThatOneCalculator - -_pkgname="hyprland" -pkgname="${_pkgname}-bin" -pkgver="0.4.0beta" -pkgrel=2 -pkgdesc="A dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks." -arch=(any) -url="https://github.com/vaxerski/Hyprland" -license=('BSD') -depends=( - libxcb - xcb-proto - xcb-util - xcb-util-keysyms - libxfixes - libx11 - libxcomposite - xorg-xinput - libxrender - pixman - wayland-protocols - cairo - pango - polkit - glslang - libinput - libxcb - libxkbcommon - opengl-driver - pixman - wayland - xcb-util-errors - xcb-util-renderutil - xcb-util-wm - seatd - vulkan-icd-loader - vulkan-validation-layers - xorg-xwayland) -source=("${pkgname}-${pkgver}.tar.gz::https://github.com/vaxerski/Hyprland/releases/download/v${pkgver}/v${pkgver}.tar.gz") -sha256sums=('5969e5f88426f90acdfb5958644733d8a9409389c2d345514c58a66cf74d2f91') -conflicts=("${_pkgname}") -provides=(hyprland) - -package() { - cd "$srcdir/Hyprland-$pkgver" - mkdir -p "${pkgdir}/usr/share/wayland-sessions" - mkdir -p "${pkgdir}/usr/share/hyprland" - install -Dm755 ./Hyprland -t "${pkgdir}/usr/bin" - install -Dm755 ./hyprctl -t "${pkgdir}/usr/bin" - install -Dm755 ./libwlroots.so.11032 -t "${pkgdir}/usr/lib" - install -Dm644 assets/*.png -t "${pkgdir}/usr/share/hyprland" - install -Dm644 example/hyprland.desktop -t "${pkgdir}/usr/share/wayland-sessions" - install -Dm644 example/hyprland.conf -t "${pkgdir}/usr/share/hyprland" - install -Dm644 LICENSE -t "${pkgdir}/usr/share/licenses/${_pkgname}" -} diff --git a/aur/PKGBUILD-git b/aur/PKGBUILD-git deleted file mode 100644 index b6443a2c..00000000 --- a/aur/PKGBUILD-git +++ /dev/null @@ -1,80 +0,0 @@ -# Maintainer: ThatOneCalculator , Sander van Kasteel - -_pkgname="hyprland" -pkgname="${_pkgname}-git" -pkgver=r673.gb62e530 -pkgrel=2 -pkgdesc="A dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks." -arch=(any) -url="https://github.com/vaxerski/Hyprland" -license=('BSD') -depends=( - libxcb - xcb-proto - xcb-util - xcb-util-keysyms - libxfixes - libx11 - libxcomposite - xorg-xinput - libxrender - pixman - wayland-protocols - cairo - pango - polkit - glslang - libinput - libxcb - libxkbcommon - opengl-driver - pixman - wayland - xcb-util-errors - xcb-util-renderutil - xcb-util-wm - seatd - vulkan-icd-loader - vulkan-validation-layers - xorg-xwayland) -makedepends=( - git - cmake - ninja - gcc - gdb - meson - vulkan-headers - wayland-protocols - xorgproto) -source=("${_pkgname}::git+https://github.com/vaxerski/Hyprland.git") -conflicts=("${_pkgname}") -provides=(hyprland) -sha256sums=('SKIP') -options=(!makeflags !buildflags) - -pkgver() { - cd "$_pkgname" - ( set -o pipefail - git describe --long 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' || - printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" - ) -} - -build() { - cd "${srcdir}/${_pkgname}" - git submodule update --init - make all -} - -package() { - cd "${srcdir}/${_pkgname}" - mkdir -p "${pkgdir}/usr/share/wayland-sessions" - mkdir -p "${pkgdir}/usr/share/hyprland" - install -Dm755 build/Hyprland -t "${pkgdir}/usr/bin" - install -Dm755 hyprctl/hyprctl -t "${pkgdir}/usr/bin" - install -Dm644 assets/*.png -t "${pkgdir}/usr/share/hyprland" - install -Dm644 example/hyprland.desktop -t "${pkgdir}/usr/share/wayland-sessions" - install -Dm644 example/hyprland.conf -t "${pkgdir}/usr/share/hyprland" - install -Dm644 LICENSE -t "${pkgdir}/usr/share/licenses/${_pkgname}" -} diff --git a/example/hyprland.conf b/example/hyprland.conf index d67355aa..931baf49 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -27,6 +27,8 @@ general { col.active_border=0x66ee1111 col.inactive_border=0x66333333 + apply_sens_to_raw=0 # do not apply the sensitivity to raw input (e.g. used by games where you aim) + damage_tracking=full # leave it on full unless you hate your GPU and want to make it suffer } diff --git a/flake.lock b/flake.lock index 06d8ffcf..71e49721 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1654593855, - "narHash": "sha256-c+SyXvj7THre87OyIdZfRVR+HhI/g1ZDrQ3VUtTuHkU=", + "lastModified": 1655807518, + "narHash": "sha256-5YV29Ry/DpAJc/0Hc/+ISVBAjwHpJvAkeKkcUG5lWsc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "033bd4fa9a8fbe0c68a88e925d9a884161044b25", + "rev": "a72d7811be1162dd6804c4e36e5402d76fb6e921", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index a756ed90..882569a8 100644 --- a/flake.nix +++ b/flake.nix @@ -16,40 +16,37 @@ }: let inherit (nixpkgs) lib; genSystems = lib.genAttrs [ + # Add more systems if they are supported "x86_64-linux" ]; pkgsFor = nixpkgs.legacyPackages; - # https://github.com/NixOS/rfcs/pull/107 - mkVersion = longDate: - lib.concatStrings [ - "0.pre" - "+date=" - (lib.concatStringsSep "-" [ - (__substring 0 4 longDate) - (__substring 4 2 longDate) - (__substring 6 2 longDate) - ]) - ]; + mkDate = longDate: (lib.concatStringsSep "-" [ + (__substring 0 4 longDate) + (__substring 4 2 longDate) + (__substring 6 2 longDate) + ]); in { - packages = genSystems (system: { - wlroots = pkgsFor.${system}.wlroots.overrideAttrs (prev: { - version = mkVersion (toString (inputs.wlroots.lastModifiedDate or inputs.wlroots.lastModified or "19700101")); + overlays.default = _: prev: rec { + wlroots-hyprland = prev.wlroots.overrideAttrs (__: { + version = mkDate (inputs.wlroots.lastModifiedDate or "19700101"); src = inputs.wlroots; }); - default = pkgsFor.${system}.callPackage ./nix/default.nix { - version = mkVersion (toString (self.lastModifiedDate or self.lastModified or "19700101")); - inherit (self.packages.${system}) wlroots; + hyprland = prev.callPackage ./nix/default.nix { + version = "0.5.0beta" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")); + wlroots = wlroots-hyprland; }; - }); + }; + + packages = genSystems (system: + (self.overlays.default null pkgsFor.${system}) + // { + default = self.packages.${system}.hyprland; + }); formatter = genSystems (system: pkgsFor.${system}.alejandra); nixosModules.default = import ./nix/module.nix self; - # Deprecated - overlays.default = _: prev: { - hyprland = self.packages.${prev.system}.default; - }; - overlay = self.overlays.default; + overlay = throw "Hyprland: .overlay output is deprecated, please use the .overlays.default output"; }; } diff --git a/meson.build b/meson.build index b902c80a..f75f64fd 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('Hyprland', 'cpp', 'c', - version : '0.1', + version : '0.5.0beta', default_options : ['warning_level=3', 'cpp_std=c++20', 'default_library=static']) wlroots = subproject('wlroots', default_options: ['examples=false']) diff --git a/nix/default.nix b/nix/default.nix index 20586b9f..18570f12 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -3,7 +3,7 @@ stdenv, fetchFromGitHub, pkg-config, - cmake, + meson, ninja, libdrm, libinput, @@ -27,7 +27,7 @@ stdenv.mkDerivation { src = ../.; nativeBuildInputs = [ - cmake + meson ninja pkg-config ]; @@ -48,33 +48,14 @@ stdenv.mkDerivation { ] ++ lib.optional enableXWayland xwayland; - cmakeFlags = - ["-DCMAKE_BUILD_TYPE=Release"] - ++ lib.optional (!enableXWayland) "-DNO_XWAYLAND=true"; + mesonBuildType = "release"; - # enables building with nix-supplied wlroots instead of submodule - prePatch = '' - sed -Ei 's/"\.\.\/wlroots\/include\/([a-zA-Z0-9./_-]+)"/<\1>/g' src/includes.hpp - ''; - postPatch = '' - make protocols - ''; + mesonFlags = lib.optional (!enableXWayland) "-DNO_XWAYLAND=true"; - postBuild = '' - pushd ../hyprctl - make all - popd - ''; - - installPhase = '' - pushd .. - install -Dm644 ./example/hyprland.desktop -t $out/share/wayland-sessions - install -Dm755 ./build/Hyprland -t $out/bin - install -Dm755 ./hyprctl/hyprctl -t $out/bin - install -Dm644 ./assets/* -t $out/share/hyprland - install -Dm644 ./example/hyprland.conf -t $out/share/hyprland - popd - ''; + patches = [ + # make meson use the provided wlroots instead of the git submodule + ./meson-build.patch + ]; passthru.providedSessions = ["hyprland"]; diff --git a/nix/meson-build.patch b/nix/meson-build.patch new file mode 100644 index 00000000..57950de9 --- /dev/null +++ b/nix/meson-build.patch @@ -0,0 +1,36 @@ +diff --git a/meson.build b/meson.build +index 22ee4bf..5528613 100644 +--- a/meson.build ++++ b/meson.build +@@ -2,16 +2,10 @@ project('Hyprland', 'cpp', 'c', + version : '0.1', + default_options : ['warning_level=3', 'cpp_std=c++20', 'default_library=static']) + +-wlroots = subproject('wlroots', default_options: ['examples=false']) +-have_xwlr = wlroots.get_variable('features').get('xwayland') ++wlroots = dependency('wlroots', version: '>=0.16.0') + xcb_dep = dependency('xcb', required: get_option('xwayland')) + +-if get_option('xwayland').enabled() and not have_xwlr +- error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support') +-endif +-have_xwayland = xcb_dep.found() and have_xwlr +- +-if not have_xwayland ++if not xcb_dep.found() + add_project_arguments('-DNO_XWAYLAND', language: 'cpp') + endif + +diff --git a/src/meson.build b/src/meson.build +index 5d64188..a676333 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -7,7 +7,7 @@ executable('Hyprland', src, + server_protos, + dependency('wayland-server'), + dependency('wayland-client'), +- wlroots.get_variable('wlroots'), ++ wlroots, + dependency('cairo'), + dependency('pango'), + dependency('pangocairo'), diff --git a/nix/module.nix b/nix/module.nix index a7460b34..383eebb7 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -19,7 +19,7 @@ in { package = mkOption { type = types.package; - default = self.packages.${pkgs.system}.default; + default = pkgs.hyprland or self.packages.${pkgs.system}.default; defaultText = literalExpression ".packages..default"; example = literalExpression ".packages..default.override { }"; description = '' diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 32ffab34..be94c093 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -443,7 +443,11 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pW double subx, suby; - const auto PFOUND = wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.vec().x, pos.y - pWindow->m_vRealPosition.vec().y, &subx, &suby); + // calc for oversized windows... fucking bullshit, again. + wlr_box geom; + wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, &geom); + + const auto PFOUND = wlr_xdg_surface_surface_at(PSURFACE, pos.x - pWindow->m_vRealPosition.vec().x + geom.x, pos.y - pWindow->m_vRealPosition.vec().y + geom.y, &subx, &suby); if (PFOUND) { sl.x = subx; @@ -454,6 +458,9 @@ wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, CWindow* pW sl.x = pos.x - pWindow->m_vRealPosition.vec().x; sl.y = pos.y - pWindow->m_vRealPosition.vec().y; + sl.x += geom.x; + sl.y += geom.y; + return PSURFACE->surface; } @@ -763,18 +770,24 @@ void CCompositor::cleanupFadingOut() { } CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) { - const auto POSA = pWindow->m_vPosition; - const auto SIZEA = pWindow->m_vSize; + + const auto WINDOWIDEALBB = pWindow->getWindowIdealBoundingBoxIgnoreReserved(); + + const auto POSA = Vector2D(WINDOWIDEALBB.x, WINDOWIDEALBB.y); + const auto SIZEA = Vector2D(WINDOWIDEALBB.width, WINDOWIDEALBB.height); auto longestIntersect = -1; CWindow* longestIntersectWindow = nullptr; for (auto& w : m_lWindows) { - if (&w == pWindow || !windowValidMapped(&w) || w.m_bIsFloating || w.m_iWorkspaceID != pWindow->m_iWorkspaceID) + if (&w == pWindow || !windowValidMapped(&w) || w.m_bIsFloating || !isWorkspaceVisible(w.m_iWorkspaceID)) continue; - const auto POSB = w.m_vPosition; - const auto SIZEB = w.m_vSize; + const auto BWINDOWIDEALBB = w.getWindowIdealBoundingBoxIgnoreReserved(); + + const auto POSB = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y); + const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height); + switch (dir) { case 'l': if (STICKS(POSA.x, POSB.x + SIZEB.x)) { diff --git a/src/Window.cpp b/src/Window.cpp index 2182d47c..8476360a 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -43,4 +43,29 @@ wlr_box CWindow::getFullWindowBoundingBox() { m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y}; return finalBox; +} + +wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() { + + const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); + + auto POS = m_vPosition; + auto SIZE = m_vSize; + + if (DELTALESSTHAN(POS.y - PMONITOR->vecPosition.y, PMONITOR->vecReservedTopLeft.y, 1)) { + POS.y = PMONITOR->vecPosition.y; + SIZE.y += PMONITOR->vecReservedTopLeft.y; + } + if (DELTALESSTHAN(POS.x - PMONITOR->vecPosition.x, PMONITOR->vecReservedTopLeft.x, 1)) { + POS.x = PMONITOR->vecPosition.x; + SIZE.x += PMONITOR->vecReservedTopLeft.x; + } + if (DELTALESSTHAN(POS.x + SIZE.x - PMONITOR->vecPosition.x, PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x, 1)) { + SIZE.x += PMONITOR->vecReservedBottomRight.x; + } + if (DELTALESSTHAN(POS.y + SIZE.y - PMONITOR->vecPosition.y, PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y, 1)) { + SIZE.y += PMONITOR->vecReservedBottomRight.y; + } + + return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; } \ No newline at end of file diff --git a/src/Window.hpp b/src/Window.hpp index 7d357292..06140303 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -108,5 +108,6 @@ public: // methods wlr_box getFullWindowBoundingBox(); + wlr_box getWindowIdealBoundingBoxIgnoreReserved(); }; \ No newline at end of file diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index d511c05a..c3621847 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -20,7 +20,7 @@ CConfigManager::CConfigManager() { void CConfigManager::setDefaultVars() { configValues["general:max_fps"].intValue = 240; configValues["general:sensitivity"].floatValue = 0.25f; - configValues["general:apply_sens_to_raw"].intValue = 1; + configValues["general:apply_sens_to_raw"].intValue = 0; configValues["general:main_mod"].strValue = "SUPER"; // exposed to the user for easier configuring configValues["general:main_mod_internal"].intValue = g_pKeybindManager->stringToModMask("SUPER"); // actually used and automatically calculated @@ -28,6 +28,7 @@ void CConfigManager::setDefaultVars() { configValues["general:damage_tracking_internal"].intValue = DAMAGE_TRACKING_NONE; configValues["general:border_size"].intValue = 1; + configValues["general:no_border_on_floating"].intValue = 0; configValues["general:gaps_in"].intValue = 5; configValues["general:gaps_out"].intValue = 20; configValues["general:col.active_border"].intValue = 0xffffffff; @@ -46,6 +47,7 @@ void CConfigManager::setDefaultVars() { configValues["decoration:inactive_opacity"].floatValue = 1; configValues["decoration:fullscreen_opacity"].floatValue = 1; configValues["decoration:multisample_edges"].intValue = 0; + configValues["decoration:no_blur_on_oversized"].intValue = 1; configValues["dwindle:pseudotile"].intValue = 0; configValues["dwindle:col.group_border"].intValue = 0x66777700; @@ -415,7 +417,7 @@ void CConfigManager::handleAnimation(const std::string& command, const std::stri configSetValueSafe("animations:" + ANIMNAME + "_style", curitem); } -void CConfigManager::handleBind(const std::string& command, const std::string& value) { +void CConfigManager::handleBind(const std::string& command, const std::string& value, bool locked) { // example: // bind=SUPER,G,exec,dmenu_run @@ -448,7 +450,7 @@ void CConfigManager::handleBind(const std::string& command, const std::string& v } if (KEY != "") - g_pKeybindManager->addKeybind(SKeybind{KEY, MOD, HANDLER, COMMAND}); + g_pKeybindManager->addKeybind(SKeybind{KEY, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap}); } void CConfigManager::handleUnbind(const std::string& command, const std::string& value) { @@ -495,16 +497,23 @@ void CConfigManager::handleWindowRule(const std::string& command, const std::str void CConfigManager::handleDefaultWorkspace(const std::string& command, const std::string& value) { const auto DISPLAY = value.substr(0, value.find_first_of(',')); - const auto WORKSPACEID = stoi(value.substr(value.find_first_of(',') + 1)); + const auto WORKSPACE = value.substr(value.find_first_of(',') + 1); for (auto& mr : m_dMonitorRules) { if (mr.name == DISPLAY) { - mr.defaultWorkspaceID = WORKSPACEID; + mr.defaultWorkspace = WORKSPACE; break; } } } +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) { static const char* const ENVHOME = getenv("HOME"); @@ -578,12 +587,14 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: } else if (COMMAND == "monitor") handleMonitor(COMMAND, VALUE); else if (COMMAND == "bind") handleBind(COMMAND, VALUE); + else if (COMMAND == "bindl") handleBind(COMMAND, VALUE, true); else if (COMMAND == "unbind") handleUnbind(COMMAND, VALUE); else if (COMMAND == "workspace") handleDefaultWorkspace(COMMAND, VALUE); else if (COMMAND == "windowrule") handleWindowRule(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 configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE); @@ -900,15 +911,26 @@ std::vector CConfigManager::getMatchingRules(CWindow* pWindow) { std::string title = g_pXWaylandManager->getTitle(pWindow); std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); + Debug::log(LOG, "Searching for matching rules for %s (title: %s)", appidclass.c_str(), title.c_str()); + for (auto& rule : m_dWindowRules) { // check if we have a matching rule try { - std::regex classCheck(rule.szValue); + if (rule.szValue.find("title:") == 0) { + // we have a title rule. + std::regex RULECHECK(rule.szValue.substr(6)); - if (!std::regex_search(title, classCheck) && !std::regex_search(appidclass, classCheck)) - continue; + if (!std::regex_search(title, RULECHECK)) + continue; + } else { + std::regex classCheck(rule.szValue); + + if (!std::regex_search(appidclass, classCheck)) + continue; + } } catch (...) { Debug::log(ERR, "Regex error at %s", rule.szValue.c_str()); + continue; } // applies. Read the rule and behave accordingly diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 866b8b8e..8c0bd857 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -28,7 +28,7 @@ struct SMonitorRule { Vector2D offset = Vector2D(0,0); float scale = 1; float refreshRate = 60; - int defaultWorkspaceID = -1; + std::string defaultWorkspace = ""; bool disabled = false; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; }; @@ -88,6 +88,8 @@ private: std::string parseError = ""; // For storing a parse error to display later + std::string m_szCurrentSubmap = ""; // For storing the current keybind submap + bool isFirstLaunch = true; // For exec-once std::deque m_dMonitorRules; @@ -106,13 +108,14 @@ private: void configSetValueSafe(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 handleBind(const std::string&, const std::string&, bool locked = false); void handleUnbind(const std::string&, const std::string&); void handleWindowRule(const std::string&, const std::string&); void handleDefaultWorkspace(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&); }; inline std::unique_ptr g_pConfigManager; \ No newline at end of file diff --git a/src/config/defaultConfig.hpp b/src/config/defaultConfig.hpp index 30ef5009..fbf3981d 100644 --- a/src/config/defaultConfig.hpp +++ b/src/config/defaultConfig.hpp @@ -34,6 +34,8 @@ general { col.active_border=0x66ee1111 col.inactive_border=0x66333333 + apply_sens_to_raw=0 # do not apply the sensitivity to raw input (e.g. used by games where you aim) + damage_tracking=full # leave it on full unless you hate your GPU and want to make it suffer } diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index e5fa7c55..afbc0745 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -114,7 +114,15 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { wlr_ext_workspace_group_handle_v1_output_enter(PNEWMONITOR->pWLRWorkspaceGroupHandle, PNEWMONITOR->output); // Workspace - const auto WORKSPACEID = monitorRule.defaultWorkspaceID == -1 ? g_pCompositor->m_lWorkspaces.size() + 1 /* Cuz workspaces doesnt have the new one yet and we start with 1 */ : monitorRule.defaultWorkspaceID; + std::string newDefaultWorkspaceName = ""; + auto WORKSPACEID = monitorRule.defaultWorkspace == "" ? g_pCompositor->m_lWorkspaces.size() + 1 : getWorkspaceIDFromString(monitorRule.defaultWorkspace, newDefaultWorkspaceName); + + if (WORKSPACEID == INT_MAX || WORKSPACEID == (long unsigned int)SPECIAL_WORKSPACE_ID) { + WORKSPACEID = g_pCompositor->m_lWorkspaces.size() + 1; + newDefaultWorkspaceName = std::to_string(WORKSPACEID); + + Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", monitorRule.defaultWorkspace); + } auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); @@ -127,19 +135,20 @@ void Events::listener_newOutput(wl_listener* listener, void* data) { g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PNEWMONITOR->ID); PNEWWORKSPACE->startAnim(true,true,true); } else { - g_pCompositor->m_lWorkspaces.emplace_back(newMonitor.ID); - PNEWWORKSPACE = &g_pCompositor->m_lWorkspaces.back(); + PNEWWORKSPACE = &g_pCompositor->m_lWorkspaces.emplace_back(newMonitor.ID); // We are required to set the name here immediately - wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, std::to_string(WORKSPACEID).c_str()); + wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, newDefaultWorkspaceName.c_str()); PNEWWORKSPACE->m_iID = WORKSPACEID; - PNEWWORKSPACE->m_szName = std::to_string(WORKSPACEID); + PNEWWORKSPACE->m_szName = newDefaultWorkspaceName; } PNEWMONITOR->activeWorkspace = PNEWWORKSPACE->m_iID; PNEWMONITOR->scale = monitorRule.scale; + PNEWMONITOR->forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. + g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle); PNEWWORKSPACE->setActive(true); @@ -216,7 +225,7 @@ void Events::listener_monitorFrame(void* owner, void* data) { return; } - if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE) { + if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && PMONITOR->forceFullFrames == 0) { pixman_region32_fini(&damage); wlr_output_rollback(PMONITOR->output); wlr_output_schedule_frame(PMONITOR->output); // we update shit at the monitor's Hz so we need to schedule frames because rollback wont @@ -224,7 +233,7 @@ void Events::listener_monitorFrame(void* owner, void* data) { } // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) { + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || PMONITOR->forceFullFrames > 0) { pixman_region32_union_rect(&damage, &damage, 0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y); pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); @@ -247,6 +256,9 @@ void Events::listener_monitorFrame(void* owner, void* data) { } } + if (PMONITOR->forceFullFrames > 0) + PMONITOR->forceFullFrames -= 1; + // TODO: this is getting called with extents being 0,0,0,0 should it be? // potentially can save on resources. diff --git a/src/events/Popups.cpp b/src/events/Popups.cpp index 0b075295..7e619632 100644 --- a/src/events/Popups.cpp +++ b/src/events/Popups.cpp @@ -26,6 +26,14 @@ void addPopupGlobalCoords(void* pPopup, int* x, int* y) { px += curPopup->popup->current.geometry.x; py += curPopup->popup->current.geometry.y; + // fix oversized fucking popups + // kill me + if (curPopup->pSurfaceTree && curPopup->pSurfaceTree->pSurface && !curPopup->parentPopup) { + const auto EXTENTSSURFACE = pixman_region32_extents(&curPopup->pSurfaceTree->pSurface->input_region); + px -= EXTENTSSURFACE->x1; + py -= EXTENTSSURFACE->y1; + } + if (curPopup->parentPopup) { curPopup = curPopup->parentPopup; } else { diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index b84b9d2d..48571892 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -19,6 +19,14 @@ void addViewCoords(void* pWindow, int* x, int* y) { const auto PWINDOW = (CWindow*)pWindow; *x += PWINDOW->m_vRealPosition.goalv().x; *y += PWINDOW->m_vRealPosition.goalv().y; + + if (!PWINDOW->m_bIsX11) { + wlr_box geom; + wlr_xdg_surface_get_geometry(PWINDOW->m_uSurface.xdg, &geom); + + *x -= geom.x; + *y -= geom.y; + } } void Events::listener_mapWindow(void* owner, void* data) { diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 6f9b465e..55b0ec83 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -158,6 +158,12 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) { outName = WORKSPACENAME; } else { if (in[0] == 'm') { + if (!g_pCompositor->m_pLastMonitor) { + Debug::log(ERR, "Relative monitor workspace on monitor null!"); + result = INT_MAX; + return result; + } + // monitor relative result = (int)getPlusMinusKeywordResult(in.substr(1), 0); @@ -207,7 +213,14 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) { outName = g_pCompositor->getWorkspaceByID(currentID)->m_szName; } else { - result = std::clamp((int)getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace), 1, INT_MAX); + if (g_pCompositor->m_pLastMonitor) + result = std::clamp((int)getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace), 1, INT_MAX); + else if (isNumber(in)) + result = std::clamp(std::stoi(in), 1, INT_MAX); + else { + Debug::log(ERR, "Relative workspace on no mon!"); + result = INT_MAX; + } outName = std::to_string(result); } } diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 4767fd78..24e555f9 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -28,6 +28,7 @@ struct SMonitor { float refreshRate = 60; wlr_output_damage* damage = nullptr; int framesToSkip = 0; + int forceFullFrames = 0; bool noFrameSchedule = false; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index ce63ace6..fb966153 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -545,11 +545,7 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, CWindow* const auto PNODE = getNodeFromWindow(PWINDOW); if (!PNODE) { - PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goalv() + pixResize); - PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(std::clamp(PWINDOW->m_vRealSize.vec().x, (double)20, (double)999999), std::clamp(PWINDOW->m_vRealSize.vec().y, (double)20, (double)999999))); - - g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv()); - + PWINDOW->m_vRealSize = Vector2D(std::clamp((PWINDOW->m_vRealSize.goalv() + pixResize).x, (double)20, (double)999999), std::clamp((PWINDOW->m_vRealSize.goalv() + pixResize).y, (double)20, (double)999999)); return; } @@ -906,6 +902,11 @@ void CHyprDwindleLayout::switchWindows(CWindow* pWindow, CWindow* pWindow2) { if (!PNODE2 || !PNODE) return; + if (PNODE->workspaceID != PNODE2->workspaceID) { + Debug::log(ERR, "Dwindle: Rejecting a swap between workspaces"); + return; + } + // we will not delete the nodes, just fix the tree if (PNODE2->pParent == PNODE->pParent) { const auto PPARENT = PNODE->pParent; @@ -992,4 +993,20 @@ void CHyprDwindleLayout::toggleSplit(CWindow* pWindow) { std::string CHyprDwindleLayout::getLayoutName() { return "dwindle"; +} + +void CHyprDwindleLayout::moveActiveWindow(const Vector2D& delta, CWindow* pWindow) { + const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow; + + if (!g_pCompositor->windowValidMapped(PWINDOW)) + return; + + if (!PWINDOW->m_bIsFloating) { + Debug::log(LOG, "Dwindle cannot move a tiled window in moveActiveWindow!"); + return; + } + + PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goalv() + delta; + + g_pHyprRenderer->damageWindow(PWINDOW); } \ No newline at end of file diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp index 766e9f6a..55c123ae 100644 --- a/src/layout/DwindleLayout.hpp +++ b/src/layout/DwindleLayout.hpp @@ -49,6 +49,7 @@ public: virtual void changeWindowFloatingMode(CWindow*); virtual void onBeginDragWindow(); virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr); + virtual void moveActiveWindow(const Vector2D&, CWindow* pWindow = nullptr); virtual void onEndDragWindow(); virtual void onMouseMove(const Vector2D&); virtual void onWindowCreatedFloating(CWindow*); diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp index 8052fe42..a7268301 100644 --- a/src/layout/IHyprLayout.hpp +++ b/src/layout/IHyprLayout.hpp @@ -55,6 +55,12 @@ public: Optional pWindow for a specific window */ virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr) = 0; + /* + Called when a user requests a move of the current window by a vec + Vector2D holds pixel values + Optional pWindow for a specific window + */ + virtual void moveActiveWindow(const Vector2D&, CWindow* pWindow = nullptr) = 0; /* Called when a window is ended being dragged (mouse up) diff --git a/src/managers/EventManager.cpp b/src/managers/EventManager.cpp index eed0e6e5..e42e87c2 100644 --- a/src/managers/EventManager.cpp +++ b/src/managers/EventManager.cpp @@ -105,9 +105,15 @@ void CEventManager::startThread() { } void CEventManager::postEvent(const SHyprIPCEvent event) { + + if (m_bIgnoreEvents) { + Debug::log(WARN, "Suppressed (ignoreevents true) event of type %s, content: %s",event.event.c_str(), event.data.c_str()); + return; + } + std::thread([&](const SHyprIPCEvent ev) { eventQueueMutex.lock(); m_dQueuedEvents.push_back(ev); eventQueueMutex.unlock(); }, event).detach(); -} \ No newline at end of file +} diff --git a/src/managers/EventManager.hpp b/src/managers/EventManager.hpp index f8ec6afc..ae630ce8 100644 --- a/src/managers/EventManager.hpp +++ b/src/managers/EventManager.hpp @@ -19,6 +19,8 @@ public: void startThread(); + bool m_bIgnoreEvents = false; + private: std::mutex eventQueueMutex; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 123e560c..85fb3919 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -28,8 +28,10 @@ CKeybindManager::CKeybindManager() { m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; m_mDispatchers["forcerendererreload"] = forceRendererReload; m_mDispatchers["resizeactive"] = resizeActive; + m_mDispatchers["moveactive"] = moveActive; m_mDispatchers["cyclenext"] = circleNext; m_mDispatchers["focuswindowbyclass"] = focusWindowByClass; + m_mDispatchers["submap"] = setSubmap; } void CKeybindManager::addKeybind(SKeybind kb) { @@ -72,13 +74,11 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const xkb_keysym_t if (handleInternalKeybinds(key)) return true; - if (g_pCompositor->m_sSeat.exclusiveClient){ - Debug::log(LOG, "Not handling keybinds due to there being an exclusive inhibited client."); - return false; - } + if (g_pCompositor->m_sSeat.exclusiveClient) + Debug::log(LOG, "Keybind handling only locked (inhibitor)"); for (auto& k : m_lKeybinds) { - if (modmask != k.modmask) + if (modmask != k.modmask || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap) continue; // oMg such performance hit!!11! @@ -280,9 +280,7 @@ void CKeybindManager::changeworkspace(std::string args) { // start anim on new workspace PWORKSPACETOCHANGETO->startAnim(true, ANIMTOLEFT); - // Event ONLY if workspace is actually "changed" and we arent just focusing - if (!m_bSuppressWorkspaceChangeEvents) - g_pEventManager->postEvent(SHyprIPCEvent("workspace", PWORKSPACETOCHANGETO->m_szName)); + g_pEventManager->postEvent(SHyprIPCEvent("workspace", PWORKSPACETOCHANGETO->m_szName)); } // If the monitor is not the one our cursor's at, warp to it. @@ -291,9 +289,6 @@ void CKeybindManager::changeworkspace(std::string args) { wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, middle.x, middle.y); } - // focus the first window - g_pCompositor->focusWindow(g_pCompositor->getFirstWindowOnWorkspace(workspaceToChangeTo)); - // set active and deactivate all other in wlr g_pCompositor->deactivateAllWLRWorkspaces(PWORKSPACETOCHANGETO->m_pWlrHandle); PWORKSPACETOCHANGETO->setActive(true); @@ -360,8 +355,7 @@ void CKeybindManager::changeworkspace(std::string args) { g_pInputManager->refocus(); // Event - if (!m_bSuppressWorkspaceChangeEvents) - g_pEventManager->postEvent(SHyprIPCEvent("workspace", PWORKSPACE->m_szName)); + g_pEventManager->postEvent(SHyprIPCEvent("workspace", PWORKSPACE->m_szName)); Debug::log(LOG, "Changed to workspace %i", workspaceToChangeTo); } @@ -491,7 +485,7 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { const auto POLDWORKSPACEONMON = g_pCompositor->getWorkspaceByID(OLDWORKSPACEIDONMONITOR); const auto POLDWORKSPACEIDRETURN = g_pCompositor->getWorkspaceByID(OLDWORKSPACEIDRETURN); - m_bSuppressWorkspaceChangeEvents = true; + g_pEventManager->m_bIgnoreEvents = true; moveActiveToWorkspace(args); @@ -510,7 +504,7 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { POLDWORKSPACEONMON->m_vRenderOffset.setValueAndWarp(Vector2D(0, 0)); POLDWORKSPACEONMON->m_fAlpha.setValueAndWarp(255.f); - m_bSuppressWorkspaceChangeEvents = false; + g_pEventManager->m_bIgnoreEvents = false; g_pInputManager->refocus(); } @@ -899,6 +893,37 @@ void CKeybindManager::resizeActive(std::string args) { std::string x = args.substr(0, args.find_first_of(' ')); std::string y = args.substr(args.find_first_of(' ') + 1); + if (x == "exact") { + std::string newX = y.substr(0, y.find_first_of(' ')); + std::string newY = y.substr(y.find_first_of(' ') + 1); + + if (!isNumber(newX) || !isNumber(newY)) { + Debug::log(ERR, "resizeTiledWindow: exact args not numbers"); + return; + } + + const int X = std::stoi(newX); + const int Y = std::stoi(newY); + + if (X < 10 || Y < 10) { + Debug::log(ERR, "resizeTiledWindow: exact args cannot be < 10"); + return; + } + + // calc the delta + if (!g_pCompositor->windowValidMapped(g_pCompositor->m_pLastWindow)) + return; // ignore + + const auto PWINDOW = g_pCompositor->m_pLastWindow; + + const int DX = X - PWINDOW->m_vRealSize.goalv().x; + const int DY = Y - PWINDOW->m_vRealSize.goalv().y; + + g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(Vector2D(DX, DY)); + + return; + } + if (!isNumber(x) || !isNumber(y)) { Debug::log(ERR, "resizeTiledWindow: args not numbers"); return; @@ -910,6 +935,55 @@ void CKeybindManager::resizeActive(std::string args) { g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(Vector2D(X, Y)); } +void CKeybindManager::moveActive(std::string args) { + if (args.find_first_of(' ') == std::string::npos) + return; + + std::string x = args.substr(0, args.find_first_of(' ')); + std::string y = args.substr(args.find_first_of(' ') + 1); + + if (x == "exact") { + std::string newX = y.substr(0, y.find_first_of(' ')); + std::string newY = y.substr(y.find_first_of(' ') + 1); + + if (!isNumber(newX) || !isNumber(newY)) { + Debug::log(ERR, "moveActive: exact args not numbers"); + return; + } + + const int X = std::stoi(newX); + const int Y = std::stoi(newY); + + if (X < 10 || Y < 10) { + Debug::log(ERR, "moveActive: exact args cannot be < 10"); + return; + } + + // calc the delta + if (!g_pCompositor->windowValidMapped(g_pCompositor->m_pLastWindow)) + return; // ignore + + const auto PWINDOW = g_pCompositor->m_pLastWindow; + + const int DX = X - PWINDOW->m_vRealPosition.goalv().x; + const int DY = Y - PWINDOW->m_vRealPosition.goalv().y; + + g_pLayoutManager->getCurrentLayout()->moveActiveWindow(Vector2D(DX, DY)); + + return; + } + + if (!isNumber(x) || !isNumber(y)) { + Debug::log(ERR, "moveActive: args not numbers"); + return; + } + + const int X = std::stoi(x); + const int Y = std::stoi(y); + + g_pLayoutManager->getCurrentLayout()->moveActiveWindow(Vector2D(X, Y)); +} + void CKeybindManager::circleNext(std::string) { if (!g_pCompositor->windowValidMapped(g_pCompositor->m_pLastWindow)) return; @@ -943,3 +1017,21 @@ void CKeybindManager::focusWindowByClass(std::string clazz) { break; } } + +void CKeybindManager::setSubmap(std::string submap) { + if (submap == "reset" || submap == "") { + m_szCurrentSelectedSubmap = ""; + Debug::log(LOG, "Reset active submap to the default one."); + return; + } + + for (auto& k : g_pKeybindManager->m_lKeybinds) { + if (k.submap == submap) { + m_szCurrentSelectedSubmap = submap; + Debug::log(LOG, "Changed keybind submap to %s", submap.c_str()); + return; + } + } + + Debug::log(ERR, "Cannot set submap %s, submap doesn't exist (wasn't registered!)", submap.c_str()); +} diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 782f2ead..c9132a11 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -11,6 +11,8 @@ struct SKeybind { uint32_t modmask = 0; std::string handler = ""; std::string arg = ""; + bool locked = false; + std::string submap = ""; }; class CKeybindManager { @@ -28,9 +30,9 @@ public: private: std::list m_lKeybinds; - bool handleInternalKeybinds(xkb_keysym_t); + inline static std::string m_szCurrentSelectedSubmap = ""; - inline static bool m_bSuppressWorkspaceChangeEvents = false; + bool handleInternalKeybinds(xkb_keysym_t); // -------------- Dispatchers -------------- // static void killActive(std::string); @@ -56,8 +58,10 @@ private: static void toggleSpecialWorkspace(std::string); static void forceRendererReload(std::string); static void resizeActive(std::string); + static void moveActive(std::string); static void circleNext(std::string); static void focusWindowByClass(std::string); + static void setSubmap(std::string); friend class CCompositor; }; diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 5927274e..65f5ddd8 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -124,14 +124,8 @@ void CHyprXWaylandManager::sendCloseWindow(CWindow* pWindow) { void CHyprXWaylandManager::setWindowSize(CWindow* pWindow, const Vector2D& size) { if (pWindow->m_bIsX11) wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, pWindow->m_vRealPosition.vec().x, pWindow->m_vRealPosition.vec().y, size.x, size.y); - else { - // I don't know if this is fucking correct, but the fucking idea of putting shadows into a window's surface is borderline criminal. - - const auto XDELTA = pWindow->m_uSurface.xdg->current.geometry.width && pWindow->m_uSurface.xdg->current.geometry.height ? pWindow->m_uSurface.xdg->surface->current.width - pWindow->m_uSurface.xdg->current.geometry.width : 0; - const auto YDELTA = pWindow->m_uSurface.xdg->current.geometry.width && pWindow->m_uSurface.xdg->current.geometry.height ? pWindow->m_uSurface.xdg->surface->current.height - pWindow->m_uSurface.xdg->current.geometry.height : 0; - - wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x - XDELTA, size.y - YDELTA); - } + else + wlr_xdg_toplevel_set_size(pWindow->m_uSurface.xdg->toplevel, size.x, size.y); } void CHyprXWaylandManager::setWindowStyleTiled(CWindow* pWindow, uint32_t edgez) { diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 826354f1..131be576 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -35,6 +35,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { Vector2D mouseCoords = getMouseCoordsInternal(); const auto PMONITOR = g_pCompositor->getMonitorFromCursor(); + bool didConstraintOnCursor = false; + // constraints // All constraints TODO: multiple mice? if (g_pCompositor->m_sSeat.mouse->currentConstraint) { @@ -50,23 +52,25 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { const auto CONSTRAINTPOS = CONSTRAINTWINDOW->m_bIsX11 ? Vector2D(CONSTRAINTWINDOW->m_uSurface.xwayland->x, CONSTRAINTWINDOW->m_uSurface.xwayland->y) : CONSTRAINTWINDOW->m_vRealPosition.vec(); const auto CONSTRAINTSIZE = CONSTRAINTWINDOW->m_bIsX11 ? Vector2D(CONSTRAINTWINDOW->m_uSurface.xwayland->width, CONSTRAINTWINDOW->m_uSurface.xwayland->height) : CONSTRAINTWINDOW->m_vRealSize.vec(); - if (!VECINRECT(mouseCoords, CONSTRAINTPOS.x, CONSTRAINTPOS.y, CONSTRAINTPOS.x + CONSTRAINTSIZE.x, CONSTRAINTPOS.y + CONSTRAINTSIZE.y)) { + if (!VECINRECT(mouseCoords, CONSTRAINTPOS.x, CONSTRAINTPOS.y, CONSTRAINTPOS.x + CONSTRAINTSIZE.x - 1.0, CONSTRAINTPOS.y + CONSTRAINTSIZE.y - 1.0)) { if (g_pCompositor->m_sSeat.mouse->constraintActive) { - Vector2D deltaToFit; + Vector2D newConstrainedCoords = mouseCoords; if (mouseCoords.x < CONSTRAINTPOS.x) - deltaToFit.x = CONSTRAINTPOS.x - mouseCoords.x; - else if (mouseCoords.x > CONSTRAINTPOS.x + CONSTRAINTSIZE.x) - deltaToFit.x = CONSTRAINTPOS.x + CONSTRAINTSIZE.x - mouseCoords.x; + newConstrainedCoords.x = CONSTRAINTPOS.x; + else if (mouseCoords.x >= CONSTRAINTPOS.x + CONSTRAINTSIZE.x) + newConstrainedCoords.x = CONSTRAINTPOS.x + CONSTRAINTSIZE.x - 1.0; if (mouseCoords.y < CONSTRAINTPOS.y) - deltaToFit.y = CONSTRAINTPOS.y - mouseCoords.y; - else if (mouseCoords.y > CONSTRAINTPOS.y + CONSTRAINTSIZE.y) - deltaToFit.y = CONSTRAINTPOS.y + CONSTRAINTSIZE.y - mouseCoords.y; + newConstrainedCoords.y = CONSTRAINTPOS.y; + else if (mouseCoords.y >= CONSTRAINTPOS.y + CONSTRAINTSIZE.y) + newConstrainedCoords.y = CONSTRAINTPOS.y + CONSTRAINTSIZE.y - 1.0; - wlr_cursor_move(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, deltaToFit.x, deltaToFit.y); + wlr_cursor_warp_closest(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, newConstrainedCoords.x, newConstrainedCoords.y); - mouseCoords = mouseCoords + deltaToFit; + mouseCoords = newConstrainedCoords; + + didConstraintOnCursor = true; } } else { if ((!CONSTRAINTWINDOW->m_bIsX11 && PMONITOR && CONSTRAINTWINDOW->m_iWorkspaceID == PMONITOR->activeWorkspace) || (CONSTRAINTWINDOW->m_bIsX11)) { @@ -84,6 +88,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // focus wlr_surface* foundSurface = nullptr; + if (didConstraintOnCursor) + return; // don't process when cursor constrained + if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor) { g_pCompositor->m_pLastMonitor = PMONITOR; @@ -116,7 +123,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { wlr_box 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_bIsMapped && w->m_bCreatedOverFullscreen) || (w->m_iWorkspaceID == SPECIAL_WORKSPACE_ID && PMONITOR->specialWorkspaceOpen)) && wlr_box_contains_point(&box, mouseCoords.x, mouseCoords.y) && g_pCompositor->isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bHidden) { pFoundWindow = &(*w); - + if (!pFoundWindow->m_bIsX11) { foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); } else { @@ -152,7 +159,6 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (!foundSurface) foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords); - if (!foundSurface) { wlr_xcursor_manager_set_cursor_image(g_pCompositor->m_sWLRXCursorMgr, "left_ptr", g_pCompositor->m_sWLRCursor); @@ -166,6 +172,14 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { Vector2D surfaceLocal = surfacePos == Vector2D(-1337, -1337) ? surfaceCoords : mouseCoords - surfacePos; + if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) { + // calc for oversized windows... fucking bullshit. + wlr_box geom; + wlr_xdg_surface_get_geometry(pFoundWindow->m_uSurface.xdg, &geom); + + surfaceLocal = mouseCoords - surfacePos + Vector2D(geom.x, geom.y); + } + if (pFoundWindow) { static auto *const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; if (*PFOLLOWMOUSE != 1 && !refocus) { @@ -173,17 +187,15 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // enter if change floating style g_pCompositor->focusWindow(pFoundWindow, foundSurface); wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); - } - else if (*PFOLLOWMOUSE == 2) { + } else if (*PFOLLOWMOUSE == 2) { wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); } wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y); - return; // don't enter any new surfaces + return; // don't enter any new surfaces } else { g_pCompositor->focusWindow(pFoundWindow, foundSurface); } - } - else + } else g_pCompositor->focusSurface(foundSurface); wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y); @@ -195,6 +207,11 @@ void CInputManager::onMouseButton(wlr_pointer_button_event* e) { const auto PKEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat); + if (!PKEYBOARD) { // ??? + Debug::log(ERR, "No active keyboard in onMouseButton??"); + return; + } + switch (e->state) { case WLR_BUTTON_PRESSED: if (!g_pCompositor->m_sSeat.mouse->currentConstraint) @@ -280,22 +297,34 @@ void CInputManager::newKeyboard(wlr_input_device* keyboard) { } void CInputManager::setKeyboardLayout() { + for (auto& k : m_lKeyboards) + applyConfigToKeyboard(&k); +} - 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"); +void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { + + ASSERT(pKeyboard); + + 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"); xkb_rule_names rules = { .rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), - .options = OPTIONS.c_str() - }; + .options = OPTIONS.c_str()}; const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + if (!CONTEXT) { + Debug::log(ERR, "applyConfigToKeyboard: CONTEXT null??"); + return; + } + const auto KEYMAP = xkb_keymap_new_from_names(CONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!KEYMAP) { @@ -304,17 +333,7 @@ void CInputManager::setKeyboardLayout() { return; } - const auto PLASTKEEB = m_pActiveKeyboard->keyboard->keyboard; - - if (!PLASTKEEB) { - xkb_keymap_unref(KEYMAP); - xkb_context_unref(CONTEXT); - - Debug::log(ERR, "No Seat Keyboard???"); - return; - } - - wlr_keyboard_set_keymap(PLASTKEEB, KEYMAP); + wlr_keyboard_set_keymap(pKeyboard->keyboard->keyboard, KEYMAP); wlr_keyboard_modifiers wlrMods = {0}; @@ -327,14 +346,14 @@ void CInputManager::setKeyboardLayout() { } if (wlrMods.locked != 0) { - wlr_keyboard_notify_modifiers(g_pInputManager->m_pActiveKeyboard->keyboard->keyboard, 0, 0, wlrMods.locked, 0); + wlr_keyboard_notify_modifiers(pKeyboard->keyboard->keyboard, 0, 0, wlrMods.locked, 0); } xkb_keymap_unref(KEYMAP); xkb_context_unref(CONTEXT); - Debug::log(LOG, "Set the keyboard layout to %s and variant to %s", rules.layout, rules.variant); -} + Debug::log(LOG, "Set the keyboard layout to %s and variant to %s for keyboard \"%s\"", rules.layout, rules.variant, pKeyboard->keyboard->name); +} void CInputManager::newMouse(wlr_input_device* mouse) { m_lMice.emplace_back(); @@ -559,4 +578,4 @@ void CInputManager::updateCapabilities(wlr_input_device* pDev) { } wlr_seat_set_capabilities(g_pCompositor->m_sSeat.seat, m_uiCapabilities); -} +} \ No newline at end of file diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 7a3601bc..ef3a4eb4 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -53,13 +53,15 @@ public: SKeyboard* m_pActiveKeyboard = nullptr; - private: +private: uint32_t m_uiCapabilities = 0; void mouseMoveUnified(uint32_t, bool refocus = false); STabletTool* ensureTabletToolPresent(wlr_tablet_tool*); + + void applyConfigToKeyboard(SKeyboard*); }; inline std::unique_ptr g_pInputManager; \ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 6a82cb83..85dd29be 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -240,6 +240,10 @@ void CHyprOpenGLImpl::scissor(const int x, const int y, const int w, const int h } void CHyprOpenGLImpl::renderRect(wlr_box* box, const CColor& col, int round) { + renderRectWithDamage(box, col, m_RenderData.pDamage, round); +} + +void CHyprOpenGLImpl::renderRectWithDamage(wlr_box* box, const CColor& col, pixman_region32_t* damage, int round) { RASSERT((box->width > 0 && box->height > 0), "Tried to render rect with width/height < 0!"); RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!"); @@ -279,8 +283,8 @@ void CHyprOpenGLImpl::renderRect(wlr_box* box, const CColor& col, int round) { glEnableVertexAttribArray(m_shQUAD.posAttrib); glEnableVertexAttribArray(m_shQUAD.texAttrib); - if (pixman_region32_not_empty(m_RenderData.pDamage)) { - PIXMAN_DAMAGE_FOREACH(m_RenderData.pDamage) { + if (pixman_region32_not_empty(damage)) { + PIXMAN_DAMAGE_FOREACH(damage) { const auto RECT = RECTSARR[i]; scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -299,15 +303,15 @@ void CHyprOpenGLImpl::renderTexture(wlr_texture* tex, wlr_box* pBox, float alpha renderTexture(CTexture(tex), pBox, alpha, round); } -void CHyprOpenGLImpl::renderTexture(const CTexture& tex, wlr_box* pBox, float alpha, int round, bool discardopaque, bool border) { +void CHyprOpenGLImpl::renderTexture(const CTexture& tex, wlr_box* pBox, float alpha, int round, bool discardopaque, bool border, bool allowPrimary) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - renderTextureInternalWithDamage(tex, pBox, alpha, m_RenderData.pDamage, round, discardopaque, border); + renderTextureInternalWithDamage(tex, pBox, alpha, m_RenderData.pDamage, round, discardopaque, border, false, allowPrimary); scissor((wlr_box*)nullptr); } -void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_box* pBox, float alpha, pixman_region32_t* damage, int round, bool discardOpaque, bool border, bool noAA) { +void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_box* pBox, float alpha, pixman_region32_t* damage, int round, bool discardOpaque, bool border, bool noAA, bool allowPrimary) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!"); @@ -341,6 +345,23 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b RASSERT(false, "tex.m_iTarget unsupported!"); } + // stencil for when we want a border + if (border) { + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_ALWAYS, 1, -1); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + // hacky fix to fix broken borders. + // TODO: this is kinda slow... question mark? + renderRect(pBox, CColor(0, 0, 0, 0), round); + + glDisable(GL_STENCIL_TEST); + } + glActiveTexture(GL_TEXTURE0); glBindTexture(tex.m_iTarget, tex.m_iTexID); @@ -357,8 +378,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b // so we need to do some maf const auto TOPLEFT = Vector2D(round, round); - const auto BOTTOMRIGHT = Vector2D(tex.m_vSize.x - round, tex.m_vSize.y - round); - const auto FULLSIZE = tex.m_vSize; + const auto BOTTOMRIGHT = Vector2D(pBox->width - round, pBox->height - round); + const auto FULLSIZE = Vector2D(pBox->width, pBox->height); static auto *const PMULTISAMPLEEDGES = &g_pConfigManager->getConfigValuePtr("decoration:multisample_edges")->intValue; // Rounded corners @@ -369,22 +390,23 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b glUniform1i(glGetUniformLocation(shader->program, "primitiveMultisample"), (int)(*PMULTISAMPLEEDGES == 1 && round != 0 && !border && !noAA)); glVertexAttribPointer(shader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + if (allowPrimary && m_RenderData.renderingPrimarySurface && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) { + const float verts[] = { + m_RenderData.primarySurfaceUVBottomRight.x, m_RenderData.primarySurfaceUVTopLeft.y, // top right + m_RenderData.primarySurfaceUVTopLeft.x, m_RenderData.primarySurfaceUVTopLeft.y, // top left + m_RenderData.primarySurfaceUVBottomRight.x, m_RenderData.primarySurfaceUVBottomRight.y, // bottom right + m_RenderData.primarySurfaceUVTopLeft.x, m_RenderData.primarySurfaceUVBottomRight.y, // bottom left + }; + + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, verts); + } else { + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + } glEnableVertexAttribArray(shader->posAttrib); glEnableVertexAttribArray(shader->texAttrib); - // stencil for when we want a border - if (border) { - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - - glEnable(GL_STENCIL_TEST); - - glStencilFunc(GL_ALWAYS, 1, -1); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - } - if (pixman_region32_not_empty(m_RenderData.pDamage)) { PIXMAN_DAMAGE_FOREACH(m_RenderData.pDamage) { const auto RECT = RECTSARR[i]; @@ -394,6 +416,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b } if (border) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, -1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); } @@ -540,9 +564,10 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox, RASSERT(m_RenderData.pMonitor, "Tried to render texture with blur without begin()!"); static auto *const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur")->intValue; + static auto* const PNOBLUROVERSIZED = &g_pConfigManager->getConfigValuePtr("decoration:no_blur_on_oversized")->intValue; - if (*PBLURENABLED == 0) { - renderTexture(tex, pBox, a, round, false, border); + if (*PBLURENABLED == 0 || (*PNOBLUROVERSIZED && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1))) { + renderTexture(tex, pBox, a, round, false, border, true); return; } @@ -599,17 +624,26 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox, if (pixman_region32_not_empty(&damage)) { // render our great blurred FB static auto *const PBLURIGNOREOPACITY = &g_pConfigManager->getConfigValuePtr("decoration:blur_ignore_opacity")->intValue; - renderTextureInternalWithDamage(POUTFB->m_cTex, &MONITORBOX, *PBLURIGNOREOPACITY ? 255.f : a, &damage); + renderTextureInternalWithDamage(POUTFB->m_cTex, &MONITORBOX, *PBLURIGNOREOPACITY ? 255.f : a, &damage, 0, false, false, false, true); // render the window, but clear stencil glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); - // and write to it + // draw window + glDisable(GL_STENCIL_TEST); + renderTextureInternalWithDamage(tex, pBox, a, &damage, round, false, false, true, true); + glEnable(GL_STENCIL_TEST); + + // prep stencil for border glStencilFunc(GL_ALWAYS, 1, -1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - renderTextureInternalWithDamage(tex, pBox, a, &damage, round, false, false, true); + if (border) { + // hacky fix to fix broken borders. + // TODO: this is kinda slow... question mark? + renderRectWithDamage(pBox, CColor(0,0,0,0), &damage, round); + } // then stop glStencilFunc(GL_EQUAL, 1, -1); @@ -649,7 +683,7 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int thick, i box->width += 2 * thick; box->height += 2 * thick; - round += thick * m_RenderData.pMonitor->scale; // cuz yeah + round += thick; // cuz yeah // only draw on non-stencild. glStencilFunc(GL_NOTEQUAL, 1, -1); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 9a8084db..0648e73a 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -34,6 +34,10 @@ struct SCurrentRenderData { float projection[9]; pixman_region32_t* pDamage = nullptr; + + bool renderingPrimarySurface = false; + Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); + Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); }; struct SMonitorRenderData { @@ -53,8 +57,9 @@ public: void end(); void renderRect(wlr_box*, const CColor&, int round = 0); + void renderRectWithDamage(wlr_box*, const CColor&, pixman_region32_t* damage, int round = 0); void renderTexture(wlr_texture*, wlr_box*, float a, int round = 0); - void renderTexture(const CTexture&, wlr_box*, float a, int round = 0, bool discardOpaque = false, bool border = false); + void renderTexture(const CTexture&, wlr_box*, float a, int round = 0, bool discardOpaque = false, bool border = false, bool allowPrimary = false); void renderTextureWithBlur(const CTexture&, wlr_box*, float a, wlr_surface* pSurface, int round = 0, bool border = false); void makeWindowSnapshot(CWindow*); @@ -110,7 +115,7 @@ private: // returns the out FB, can be either Mirror or MirrorSwap CFramebuffer* blurMainFramebufferWithDamage(float a, wlr_box* pBox, pixman_region32_t* damage); - void renderTextureInternalWithDamage(const CTexture&, wlr_box* pBox, float a, pixman_region32_t* damage, int round = 0, bool discardOpaque = false, bool border = false, bool noAA = false); + void renderTextureInternalWithDamage(const CTexture&, wlr_box* pBox, float a, pixman_region32_t* damage, int round = 0, bool discardOpaque = false, bool border = false, bool noAA = false, bool allowPrimary = false); void renderBorder(wlr_box*, const CColor&, int thick = 1, int round = 0); }; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index eb29a65d..8118af48 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -23,8 +23,12 @@ void renderSurface(struct wlr_surface* surface, int x, int y, void* data) { float rounding = RDATA->dontRound ? 0 : RDATA->rounding == -1 ? *PROUNDING : RDATA->rounding; - if (RDATA->surface && surface == RDATA->surface) + g_pHyprOpenGL->m_RenderData.renderingPrimarySurface = false; + + if (RDATA->surface && surface == RDATA->surface) { + g_pHyprOpenGL->m_RenderData.renderingPrimarySurface = true; g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, surface, rounding, RDATA->decorate); + } else g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, RDATA->fadeAlpha * RDATA->alpha, rounding, false, false); @@ -142,6 +146,8 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); const auto REALPOS = pWindow->m_vRealPosition.vec() + PWORKSPACE->m_vRenderOffset.vec(); + static const auto PNOFLOATINGBORDERS = &g_pConfigManager->getConfigValuePtr("general:no_border_on_floating")->intValue; + SRenderData renderdata = {pMonitor->output, time, REALPOS.x, REALPOS.y}; renderdata.surface = g_pXWaylandManager->getWindowSurface(pWindow); renderdata.w = std::clamp(pWindow->m_vRealSize.vec().x, (double)5, (double)1337420); // clamp the size to min 5, @@ -149,7 +155,7 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* renderdata.dontRound = pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; renderdata.fadeAlpha = pWindow->m_fAlpha.fl() * (PWORKSPACE->m_fAlpha.fl() / 255.f); renderdata.alpha = pWindow->m_bIsFullscreen ? g_pConfigManager->getFloat("decoration:fullscreen_opacity") : pWindow == g_pCompositor->m_pLastWindow ? g_pConfigManager->getFloat("decoration:active_opacity") : g_pConfigManager->getFloat("decoration:inactive_opacity"); - renderdata.decorate = decorate && !pWindow->m_bX11DoesntWantBorders; + renderdata.decorate = decorate && !pWindow->m_bX11DoesntWantBorders && (pWindow->m_bIsFloating ? *PNOFLOATINGBORDERS == 0 : true); renderdata.rounding = pWindow->m_sAdditionalConfigData.rounding; // apply window special data @@ -164,8 +170,30 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* for (auto& wd : pWindow->m_dWindowDecorations) wd->draw(pMonitor); + if (!pWindow->m_bIsX11) { + + // To everyone who makes apps with improperly aligned surfaces, + // For example chromium, or GTK devs who allow shadows on windows, + // a sincere FUCK YOU. + + wlr_box geom; + wlr_xdg_surface_get_geometry(pWindow->m_uSurface.xdg, &geom); + + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D((double)geom.x / (double)pWindow->m_uSurface.xdg->surface->current.width, (double)geom.y / (double)pWindow->m_uSurface.xdg->surface->current.height); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D((double)(geom.width + geom.x) / (double)pWindow->m_uSurface.xdg->surface->current.width, (double)(geom.y + geom.height) / (double)pWindow->m_uSurface.xdg->surface->current.height); + + if (g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft == Vector2D() && g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { + // No special UV mods needed + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + } + } + wlr_surface_for_each_surface(g_pXWaylandManager->getWindowSurface(pWindow), renderSurface, &renderdata); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + if (pWindow->m_bIsX11) { if (pWindow->m_uSurface.xwayland->surface) { wlr_surface_for_each_surface(pWindow->m_uSurface.xwayland->surface, renderSurface, &renderdata);