Merge branch 'main' into movegroupfocus

This commit is contained in:
speckmaier 2024-01-17 09:09:02 +01:00
commit 31f08aab4e
171 changed files with 9560 additions and 4241 deletions

1
.clang-format-ignore Normal file
View file

@ -0,0 +1 @@
subprojects/**/*

View file

@ -9,12 +9,23 @@ body:
--- ---
- type: input - type: textarea
id: ver id: ver
attributes: attributes:
label: Hyprland Version label: Hyprland Version
description: "Paste here the output of `hyprctl version`." description: "Paste here the output of `hyprctl version`. For hyprland after 0.34.0, paste `hyprctl systeminfo` instead."
placeholder: Hyprland, built from branch main at commit... value: "<details>
<summary>System/Version info</summary>
```sh
<Paste the output of the command here>
```
</details>"
validations: validations:
required: true required: true

75
.github/actions/setup_base/action.yml vendored Normal file
View file

@ -0,0 +1,75 @@
name: "Setup base"
inputs:
INSTALL_XORG_PKGS:
description: 'Install xorg dependencies'
required: false
default: false
runs:
using: "composite"
steps:
- name: Get required pacman pkgs
shell: bash
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy \
base-devel \
cairo \
clang \
cmake \
git \
glm \
glslang \
go \
jq \
libc++ \
libdisplay-info \
libdrm \
libepoxy \
libfontenc \
libglvnd \
libinput \
libliftoff \
libxcvt \
libxfont2 \
libxkbcommon \
libxkbfile \
lld \
meson \
ninja \
pango \
pixman \
pkgconf \
scdoc \
seatd \
systemd \
tomlplusplus \
wayland \
wayland-protocols \
xcb-util-errors \
xcb-util-renderutil \
xcb-util-wm
- name: Get Xorg pacman pkgs
shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true'
run: |
pacman --noconfirm --noprogressbar -Sy \
xorg-fonts-encodings \
xorg-server-common \
xorg-setxkbmap \
xorg-xkbcomp \
xorg-xwayland
- name: Checkout Hyprland
uses: actions/checkout@v4
with:
submodules: recursive
# Fix an issue with actions/checkout where the checkout repo is not mark as safe
- name: Mark directory as safe for git
shell: bash
run: |
git config --global --add safe.directory /__w/Hyprland/Hyprland

View file

@ -8,29 +8,20 @@ jobs:
container: container:
image: archlinux image: archlinux
steps: steps:
- name: Get required pacman pkgs - name: Checkout repository actions
run: | uses: actions/checkout@v4
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
with: with:
submodules: recursive sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
with:
INSTALL_XORG_PKGS: true
- name: Build Hyprland - name: Build Hyprland
run: | run: |
git submodule sync --recursive && git submodule update --init --force --recursive
make all make all
- name: Compress and package artifacts - name: Compress and package artifacts
run: | run: |
mkdir x86_64-pc-linux-gnu mkdir x86_64-pc-linux-gnu
@ -39,12 +30,13 @@ jobs:
mkdir hyprland/assets mkdir hyprland/assets
cp ./LICENSE hyprland/ cp ./LICENSE hyprland/
cp build/Hyprland hyprland/ cp build/Hyprland hyprland/
cp hyprctl/hyprctl hyprland/ cp build/hyprctl/hyprctl hyprland/
cp subprojects/wlroots/build/libwlroots.so.12032 hyprland/ cp subprojects/wlroots/build/libwlroots.so.13032 hyprland/
cp build/Hyprland hyprland/ cp build/Hyprland hyprland/
cp -r example/ hyprland/ cp -r example/ hyprland/
cp -r assets/ hyprland/ cp -r assets/ hyprland/
tar -cvf Hyprland.tar.xz hyprland tar -cvf Hyprland.tar.xz hyprland
- name: Release - name: Release
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
@ -57,28 +49,19 @@ jobs:
container: container:
image: archlinux image: archlinux
steps: steps:
- name: Download dependencies - name: Checkout repository actions
run: | uses: actions/checkout@v4
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
with: with:
submodules: true sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure - name: Configure
run: | run: meson setup build -Ddefault_library=static
meson obj-x86_64-pc-linux-gnu \
-Ddefault_library=static
- name: Compile - name: Compile
run: ninja -C obj-x86_64-pc-linux-gnu run: ninja -C build
noxwayland: noxwayland:
name: "Build Hyprland in pure Wayland (Arch)" name: "Build Hyprland in pure Wayland (Arch)"
@ -86,23 +69,36 @@ jobs:
container: container:
image: archlinux image: archlinux
steps: steps:
- name: Download dependencies - name: Checkout repository actions
run: | uses: actions/checkout@v4
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
with: with:
submodules: true sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure - 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 - name: Compile
run: make config && make release 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

View file

@ -10,7 +10,6 @@ jobs:
matrix: matrix:
package: package:
- hyprland - hyprland
- hyprland-nvidia
- xdg-desktop-portal-hyprland - xdg-desktop-portal-hyprland
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -27,4 +26,4 @@ jobs:
name: hyprland name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix build -L ${{ matrix.command }} - run: nix build .#${{ matrix.package }} -L

View file

@ -15,10 +15,15 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: Generate version
id: genversion
run: |
bash -c scripts/generateVersion.sh
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak
- name: Create tarball with submodules - name: Create tarball with submodules
id: tar id: tar
run: | run: |
sed -i "1s/^/#define GIT_COMMIT_HASH \"$(git rev-parse HEAD)\"\n#define GIT_TAG \"$(git describe --tags)\"\n/" ./src/macros.hpp
mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz * mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz *
- id: whatrelease - id: whatrelease

View file

@ -24,13 +24,13 @@ jobs:
uses: github/codeql-action/upload-sarif@v2 uses: github/codeql-action/upload-sarif@v2
with: with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif sarif_file: ${{github.workspace}}/flawfinder_results.sarif
codeql: codeql:
name: CodeQL name: CodeQL
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: archlinux image: archlinux
permissions: permissions:
actions: read actions: read
contents: read contents: read
@ -42,34 +42,25 @@ jobs:
language: [ 'cpp' ] language: [ 'cpp' ]
steps: steps:
- name: Checkout repository - name: Checkout repository actions
uses: actions/checkout@v3 uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Init Hyprland build - name: Setup base
run: | uses: ./.github/actions/setup_base
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
with: with:
submodules: recursive INSTALL_XORG_PKGS: true
- name: Build Hyprland - name: Build Hyprland
run: | run: |
git submodule sync --recursive && git submodule update --init --force --recursive
make all make all
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v2
with: with:

28
.github/workflows/stale.yml vendored Normal file
View file

@ -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

2
.gitignore vendored
View file

@ -29,3 +29,5 @@ gmon.out
*.tar.gz *.tar.gz
PKGBUILD PKGBUILD
src/version.h

120
CMakeLists.txt Normal file → Executable file
View file

@ -21,37 +21,51 @@ message(STATUS "Gathering git info")
# Get git info # Get git info
# hash and branch # hash and branch
execute_process( execute_process(
COMMAND git rev-parse --abbrev-ref HEAD COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g'"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git diff-index --quiet HEAD -- || echo \"dirty\""
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DIRTY
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git describe --tags"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE)
# #
# #
# udis
add_subdirectory("subprojects/udis86")
# wlroots
message(STATUS "Setting up wlroots")
include(ExternalProject)
if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
if(BUILDTYPE_LOWER STREQUAL "release")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "debug")
# Pass.
elseif(BUILDTYPE_LOWER STREQUAL "relwithdebinfo")
set(BUILDTYPE_LOWER "debugoptimized")
elseif(BUILDTYPE_LOWER STREQUAL "minsizerel")
set(BUILDTYPE_LOWER "minsize")
elseif(BUILDTYPE_LOWER STREQUAL "none")
set(BUILDTYPE_LOWER "plain")
else()
set(BUILDTYPE_LOWER "release")
endif()
else()
set(BUILDTYPE_LOWER "release")
endif()
ExternalProject_Add(
wlroots
PREFIX ${CMAKE_SOURCE_DIR}/subprojects/wlroots
SOURCE_DIR ${CMAKE_SOURCE_DIR}/subprojects/wlroots
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=$<IF:$<BOOL:${NO_XWAYLAND}>,disabled,enabled> -Dexamples=false -Drenderers=gles2 $<IF:$<BOOL:${WITH_ASAN}>,-Db_sanitize=address,-Db_sanitize=none>
BUILD_COMMAND ninja -C build
BUILD_ALWAYS true
BUILD_IN_SOURCE true
BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032
INSTALL_COMMAND echo "wlroots: install not needed"
)
find_program(WaylandScanner NAMES wayland-scanner) find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}") message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process( execute_process(
@ -85,14 +99,20 @@ set(CMAKE_ENABLE_EXPORTS TRUE)
message(STATUS "Checking deps...") message(STATUS "Checking deps...")
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
find_package(OpenGL 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) # we do not check for wlroots, as we provide it ourselves
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(Hyprland ${SRCFILES}) set(TRACY_CPP_FILES "")
if(USE_TRACY)
set(TRACY_CPP_FILES "subprojects/tracy/public/TracyClient.cpp")
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
endif()
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
add_dependencies(Hyprland wlroots)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags") message(STATUS "Setting debug flags")
@ -101,7 +121,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan") message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan) target_link_libraries(Hyprland asan)
add_compile_options(-fsanitize=address) target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif() endif()
if(USE_TRACY) if(USE_TRACY)
@ -123,6 +143,12 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
add_link_options(-pg -no-pie -fno-builtin) add_link_options(-pg -no-pie -fno-builtin)
endif() endif()
check_include_file("execinfo.h" EXECINFOH)
if(EXECINFOH)
message(STATUS "Configuration supports execinfo")
add_compile_definitions(HAS_EXECINFO)
endif()
include(CheckLibraryExists) include(CheckLibraryExists)
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO) check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
if(HAVE_LIBEXECINFO) if(HAVE_LIBEXECINFO)
@ -147,24 +173,21 @@ if(NO_SYSTEMD)
message(STATUS "SYSTEMD support is disabled...") message(STATUS "SYSTEMD support is disabled...")
else() else()
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined) checking deps...") message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined) checking deps...")
pkg_check_modules(LIBSYSTEMD libsystemd)
check_include_file("systemd/sd-daemon.h" SYSTEMDH) check_include_file("systemd/sd-daemon.h" SYSTEMDH)
if(LIBSYSTEMD_FOUND AND SYSTEMDH) if(SYSTEMDH)
add_compile_definitions(USES_SYSTEMD) pkg_check_modules(LIBSYSTEMD libsystemd)
target_link_libraries(Hyprland "${LIBSYSTEMD_LIBRARIES}") if (LIBSYSTEMD_FOUND)
add_compile_definitions(USES_SYSTEMD)
target_link_libraries(Hyprland "${LIBSYSTEMD_LIBRARIES}")
message(STATUS "Systemd found")
else()
message(WARNING "Systemd support requested but systemd libraries were not found")
endif()
else() else()
message(WARNING "Systemd support requested but libsystemd or systemd headers were not found") message(WARNING "Systemd support requested but systemd headers were not found")
endif() endif()
endif() endif()
target_compile_definitions(Hyprland
PRIVATE
"GIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\""
"GIT_BRANCH=\"${GIT_BRANCH}\""
"GIT_COMMIT_MESSAGE=\"${GIT_COMMIT_MESSAGE}\""
"GIT_DIRTY=\"${GIT_DIRTY}\""
"GIT_TAG=\"${GIT_TAG}\"")
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack) include(CPack)
@ -198,11 +221,11 @@ function(protocol protoPath protoName external)
endfunction() endfunction()
target_link_libraries(Hyprland target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032 # wlroots is provided by us ${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.13032 # wlroots is provided by us
OpenGL::EGL OpenGL::EGL
OpenGL::GL OpenGL::GL
Threads::Threads Threads::Threads
${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a libudis86
) )
protocol("protocols/idle.xml" "idle" true) protocol("protocols/idle.xml" "idle" true)
@ -218,5 +241,10 @@ protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) protocol("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocol("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)
protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
# tools
add_subdirectory(hyprctl)
add_subdirectory(hyprpm)

View file

@ -3,103 +3,76 @@ PREFIX = /usr/local
legacyrenderer: 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 -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
legacyrendererdebug: 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 -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
release: 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 -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
debug: 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 -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
clear: clear:
rm -rf build rm -rf build
rm -f ./protocols/*-protocol.h ./protocols/*-protocol.c rm -f ./protocols/*-protocol.h ./protocols/*-protocol.c
rm -f ./hyprctl/hyprctl
rm -rf ./subprojects/wlroots/build rm -rf ./subprojects/wlroots/build
all: all:
@if [[ "$EUID" = 0 ]]; then echo -en "Avoid running $(MAKE) all as sudo.\n"; fi
$(MAKE) clear $(MAKE) clear
$(MAKE) fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
$(MAKE) release $(MAKE) release
$(MAKE) -C hyprctl all
install: install:
$(MAKE) clear @if [ ! -f ./build/Hyprland ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
$(MAKE) fixwlr @echo -en "!NOTE: Please note make install does not compile Hyprland and only installs the already built files."
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` && cd ../..
$(MAKE) release
$(MAKE) -C hyprctl all
mkdir -p ${PREFIX}/share/wayland-sessions mkdir -p ${PREFIX}/share/wayland-sessions
mkdir -p ${PREFIX}/bin mkdir -p ${PREFIX}/bin
cp -f ./build/Hyprland ${PREFIX}/bin cp -f ./build/Hyprland ${PREFIX}/bin
cp -f ./hyprctl/hyprctl ${PREFIX}/bin cp -f ./build/hyprctl/hyprctl ${PREFIX}/bin
cp -f ./build/hyprpm/hyprpm ${PREFIX}/bin
chmod 755 ${PREFIX}/bin/Hyprland
chmod 755 ${PREFIX}/bin/hyprctl
chmod 755 ${PREFIX}/bin/hyprpm
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 if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland 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
mkdir -p ${PREFIX}/share/man/man1 mkdir -p ${PREFIX}/share/man/man1
install -m644 ./docs/*.1 ${PREFIX}/share/man/man1 install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
mkdir -p ${PREFIX}/include/hyprland mkdir -p ${PREFIX}/lib/
mkdir -p ${PREFIX}/include/hyprland/protocols cp ./subprojects/wlroots/build/libwlroots.so.13032 ${PREFIX}/lib/
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/share/pkgconfig
mkdir -p ${PREFIX}/share/xdg-desktop-portal
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cd subprojects/wlroots/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
cp ./assets/hyprland-portals.conf ${PREFIX}/share/xdg-desktop-portal/
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
cleaninstall: $(MAKE) installheaders
echo -en "$(MAKE) cleaninstall has been DEPRECATED, you should avoid using it in the future.\nRunning $(MAKE) install instead...\n"
$(MAKE) install
uninstall: uninstall:
rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
rm -f ${PREFIX}/bin/Hyprland rm -f ${PREFIX}/bin/Hyprland
rm -f ${PREFIX}/bin/hyprctl rm -f ${PREFIX}/bin/hyprctl
rm -f ${PREFIX}/lib/libwlroots.so.12032 rm -f ${PREFIX}/bin/hyprpm
rm -f ${PREFIX}/lib/libwlroots.so.13032
rm -rf ${PREFIX}/share/hyprland rm -rf ${PREFIX}/share/hyprland
rm -f ${PREFIX}/share/man/man1/Hyprland.1 rm -f ${PREFIX}/share/man/man1/Hyprland.1
rm -f ${PREFIX}/share/man/man1/hyprctl.1 rm -f ${PREFIX}/share/man/man1/hyprctl.1
fixwlr:
sed -E -i -e 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build
rm -rf ./subprojects/wlroots/build
config:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false -Drenderers="gles2"
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
pluginenv: pluginenv:
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` @echo -en "$(MAKE) pluginenv has been deprecated.\nPlease run $(MAKE) all && sudo $(MAKE) installheaders\n"
@exit 1
$(MAKE) fixwlr
installheaders:
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false @if [ ! -f ./src/version.h ]; then echo -en "You need to run $(MAKE) all first.\n" && exit 1; fi
ninja -C subprojects/wlroots/build/
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
mkdir -p ${PREFIX}/include/hyprland mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols mkdir -p ${PREFIX}/include/hyprland/protocols
@ -113,13 +86,8 @@ pluginenv:
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
configdebug: chmod -R 755 ${PREFIX}/include/hyprland
$(MAKE) fixwlr chmod 755 ${PREFIX}/share/pkgconfig
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=debug -Dwerror=false -Dexamples=false -Drenderers="gles2" -Db_sanitize=address
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
man: man:
pandoc ./docs/Hyprland.1.rst \ pandoc ./docs/Hyprland.1.rst \

View file

@ -41,6 +41,8 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
- Much more QoL stuff than other wlr-based compositors - Much more QoL stuff than other wlr-based compositors
- Custom bezier curves for the best animations - Custom bezier curves for the best animations
- Powerful plugin support - Powerful plugin support
- Built-in plugin manager
- Tearing support for better gaming performance
- Easily expandable and readable codebase - Easily expandable and readable codebase
- Fast and active development - Fast and active development
- Not scared to provide bleeding-edge features - Not scared to provide bleeding-edge features
@ -127,8 +129,8 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
<!----------------------------------{ Images }---------------------------------> <!----------------------------------{ Images }--------------------------------->
[Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg [Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg
[Preview A]: https://cdn.discordapp.com/attachments/1091569872535814185/1107675866101723277/screenshot-summer.png [Preview A]: https://i.ibb.co/C1yTb0r/falf.png
[Preview B]: https://i.ibb.co/SX7GbYR/winter-rice.png [Preview B]: https://cdn.discordapp.com/attachments/1091569872535814185/1107675866101723277/screenshot-summer.png
[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png [Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png

View file

@ -1,2 +1,2 @@
[preferred] [preferred]
default=hyprland default=hyprland;gtk

View file

@ -1,8 +0,0 @@
# compile with HYPRLAND_HEADERS=<path_to_hl> make all
# make sure that the path above is to the root hl repo directory, NOT src/
# and that you have ran `make protocols` in the hl dir.
all:
$(CXX) -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b
clean:
rm ./examplePlugin.so

View file

@ -1,74 +0,0 @@
#include "customDecoration.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
CCustomDecoration::CCustomDecoration(CWindow* pWindow) {
m_pWindow = pWindow;
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
}
CCustomDecoration::~CCustomDecoration() {
damageEntire();
}
SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() {
return m_seExtents;
}
void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
if (!g_pCompositor->windowValidMapped(m_pWindow))
return;
if (!m_pWindow->m_sSpecialRenderData.decorate)
return;
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue;
static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ?
0 :
(m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying());
// draw the border
wlr_box fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE),
(int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)};
fullBox.x -= pMonitor->vecPosition.x;
fullBox.y -= pMonitor->vecPosition.y;
m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2},
{fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2,
fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}};
fullBox.x += offset.x;
fullBox.y += offset.y;
if (fullBox.width < 1 || fullBox.height < 1)
return; // don't draw invisible shadows
g_pHyprOpenGL->scissor((wlr_box*)nullptr);
scaleBox(&fullBox, pMonitor->scale);
g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a);
}
eDecorationType CCustomDecoration::getDecorationType() {
return DECORATION_CUSTOM;
}
void CCustomDecoration::updateWindow(CWindow* pWindow) {
m_vLastWindowPos = pWindow->m_vRealPosition.vec();
m_vLastWindowSize = pWindow->m_vRealSize.vec();
damageEntire();
}
void CCustomDecoration::damageEntire() {
wlr_box dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y),
(int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y};
g_pHyprRenderer->damageBox(&dm);
}

View file

@ -1,29 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/render/decorations/IHyprWindowDecoration.hpp>
class CCustomDecoration : public IHyprWindowDecoration {
public:
CCustomDecoration(CWindow*);
virtual ~CCustomDecoration();
virtual SWindowDecorationExtents getWindowDecorationExtents();
virtual void draw(CMonitor*, float a, const Vector2D& offset);
virtual eDecorationType getDecorationType();
virtual void updateWindow(CWindow*);
virtual void damageEntire();
private:
SWindowDecorationExtents m_seExtents;
CWindow* m_pWindow = nullptr;
Vector2D m_vLastWindowPos;
Vector2D m_vLastWindowSize;
};

View file

@ -1,80 +0,0 @@
#include "customLayout.hpp"
#include <hyprland/src/Compositor.hpp>
#include "globals.hpp"
void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto SIZE = PMONITOR->vecSize;
// these are used for focus and move calculations, and are *required* to touch for moving focus to work properly.
pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)};
pWindow->m_vSize = SIZE / 2.0;
// this is the actual pos and size of the window (where it's rendered)
pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10};
pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20};
const auto PDATA = &m_vWindowData.emplace_back();
PDATA->pWindow = pWindow;
}
void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) {
std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; });
}
bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) {
return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end();
}
void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) {
; // empty
}
void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) {
; // empty
}
void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) {
; // empty
}
std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) {
return "";
}
SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) {
return {};
}
void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
; // empty
}
void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
; // empty
}
std::string CHyprCustomLayout::getLayoutName() {
return "custom";
}
void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
; // empty
}
void CHyprCustomLayout::onEnable() {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating)
continue;
onWindowCreatedTiling(w.get());
}
}
void CHyprCustomLayout::onDisable() {
m_vWindowData.clear();
}

View file

@ -1,32 +0,0 @@
#pragma once
#define WLR_USE_UNSTABLE
#include <hyprland/src/layout/IHyprLayout.hpp>
struct SWindowData {
CWindow* pWindow = nullptr;
};
class CHyprCustomLayout : public IHyprLayout {
public:
virtual void onWindowCreatedTiling(CWindow*);
virtual void onWindowRemovedTiling(CWindow*);
virtual bool isWindowTiled(CWindow*);
virtual void recalculateMonitor(const int&);
virtual void recalculateWindow(CWindow*);
virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
virtual void switchWindows(CWindow*, CWindow*);
virtual void alterSplitRatio(CWindow*, float, bool);
virtual std::string getLayoutName();
virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
virtual void onEnable();
virtual void onDisable();
private:
std::vector<SWindowData> m_vWindowData;
};

View file

@ -1,5 +0,0 @@
#pragma once
#include <hyprland/src/plugins/PluginAPI.hpp>
inline HANDLE PHANDLE = nullptr;

View file

@ -1,99 +0,0 @@
#define WLR_USE_UNSTABLE
#include "globals.hpp"
#include <hyprland/src/Window.hpp>
#include <hyprland/src/Compositor.hpp>
#include "customLayout.hpp"
#include "customDecoration.hpp"
#include <unistd.h>
#include <thread>
// Methods
inline std::unique_ptr<CHyprCustomLayout> g_pCustomLayout;
inline CFunctionHook* g_pFocusHook = nullptr;
inline CFunctionHook* g_pMotionHook = nullptr;
inline CFunctionHook* g_pMouseDownHook = nullptr;
typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
typedef void (*origMotion)(wlr_seat*, uint32_t, double, double);
typedef void (*origMouseDownNormal)(void*, wlr_pointer_button_event*);
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
static void onActiveWindowChange(void* self, std::any data) {
try {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: " + (PWINDOW ? PWINDOW->m_szTitle : "None"), CColor{0.f, 0.5f, 1.f, 1.f}, 5000);
} catch (std::bad_any_cast& e) { HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: None", CColor{0.f, 0.5f, 1.f, 1.f}, 5000); }
}
static void onNewWindow(void* self, std::any data) {
auto* const PWINDOW = std::any_cast<CWindow*>(data);
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CCustomDecoration(PWINDOW));
}
void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("FocusWindow with %lx %lx", pWindow, pSurface), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origFocusWindow)g_pFocusHook->m_pOriginal)(thisptr, pWindow, pSurface);
}
void hkNotifyMotion(wlr_seat* wlr_seat, uint32_t time_msec, double sx, double sy) {
// HyprlandAPI::addNotification(PHANDLE, getFormat("NotifyMotion with %lf %lf", sx, sy), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
(*(origMotion)g_pMotionHook->m_pOriginal)(wlr_seat, time_msec, sx, sy);
}
void hkProcessMouseDownNormal(void* thisptr, wlr_pointer_button_event* e) {
// HyprlandAPI::addNotification(PHANDLE, "Mouse down normal!", CColor{0.8f, 0.2f, 0.5f, 1.0f}, 5000);
(*(origMouseDownNormal)g_pMouseDownHook->m_pOriginal)(thisptr, e);
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
HyprlandAPI::addNotification(PHANDLE, "Hello World from an example plugin!", CColor{0.f, 1.f, 1.f, 1.f}, 5000);
HyprlandAPI::registerCallbackDynamic(PHANDLE, "activeWindow", [&](void* self, std::any data) { onActiveWindowChange(self, data); });
HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, std::any data) { onNewWindow(self, data); });
g_pCustomLayout = std::make_unique<CHyprCustomLayout>();
HyprlandAPI::addLayout(PHANDLE, "custom", g_pCustomLayout.get());
HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:border_color", SConfigValue{.intValue = configStringToInt("rgb(44ee44)")});
HyprlandAPI::addDispatcher(PHANDLE, "example", [](std::string arg) { HyprlandAPI::addNotification(PHANDLE, "Arg passed: " + arg, CColor{0.5f, 0.5f, 0.7f, 1.0f}, 5000); });
// Hook a public member
g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
// Hook a public non-member
g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
// Hook a private member
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "processMouseDownNormal");
g_pMouseDownHook = HyprlandAPI::createFunctionHook(PHANDLE, METHODS[0].address, (void*)&hkProcessMouseDownNormal);
static auto* const PBORDERCOLOR = HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color");
// fancy notifications
HyprlandAPI::addNotificationV2(
PHANDLE,
{{"text", "Example hint, color " + std::to_string(PBORDERCOLOR->intValue)}, {"time", (uint64_t)10000}, {"color", CColor{PBORDERCOLOR->intValue}}, {"icon", ICON_HINT}});
// Enable our hooks
g_pFocusHook->hook();
g_pMotionHook->hook();
g_pMouseDownHook->hook();
HyprlandAPI::reloadConfig();
return {"ExamplePlugin", "An example plugin", "Vaxry", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
HyprlandAPI::invokeHyprctlCommand("seterror", "disable");
}

View file

@ -19,8 +19,14 @@ monitor=,preferred,auto,auto
# Source a file (multi-file configs) # Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf # source = ~/.config/hypr/myColors.conf
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars. # Some default env vars.
env = XCURSOR_SIZE,24 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/ # For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input { input {
@ -49,6 +55,9 @@ general {
col.inactive_border = rgba(595959aa) col.inactive_border = rgba(595959aa)
layout = dwindle layout = dwindle
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
} }
decoration { decoration {
@ -60,6 +69,8 @@ decoration {
enabled = true enabled = true
size = 3 size = 3
passes = 1 passes = 1
vibrancy = 0.1696
} }
drop_shadow = true drop_shadow = true
@ -99,6 +110,11 @@ gestures {
workspace_swipe = false workspace_swipe = false
} }
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers
}
# Example per-device config # Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more # See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device:epic-mouse-v1 { device:epic-mouse-v1 {
@ -110,18 +126,19 @@ device:epic-mouse-v1 {
# Example windowrule v2 # Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more # See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = nomaximizerequest, class:.* # You'll probably like this.
# See https://wiki.hyprland.org/Configuring/Keywords/ for more # See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER $mainMod = SUPER
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more # Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, kitty bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive, bind = $mainMod, C, killactive,
bind = $mainMod, M, exit, bind = $mainMod, M, exit,
bind = $mainMod, E, exec, dolphin bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating, bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, wofi --show drun bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, togglesplit, # dwindle bind = $mainMod, J, togglesplit, # dwindle
@ -155,6 +172,10 @@ bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9 bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10 bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, magic
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
# Scroll through existing workspaces with mainMod + scroll # Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1 bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1 bind = $mainMod, mouse_up, workspace, e-1

View file

@ -23,13 +23,34 @@
"type": "github" "type": "github"
} }
}, },
"hyprlang": {
"inputs": {
"nixpkgs": [
"xdph",
"nixpkgs"
]
},
"locked": {
"lastModified": 1704287638,
"narHash": "sha256-TuRXJGwtK440AXQNl5eiqmQqY4LZ/9+z/R7xC0ie3iA=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "6624f2bb66d4d27975766e81f77174adbe58ec97",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprlang",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1694767346, "lastModified": 1705133751,
"narHash": "sha256-5uH27SiVFUwsTsqC5rs3kS7pBoNhtoy9QfTP9BmknGk=", "narHash": "sha256-rCIsyE80jgiOU78gCWN3A0wE0tR2GI5nH6MlS+HaaSQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ace5093e36ab1e95cb9463863491bee90d5a4183", "rev": "9b19f5e77dd906cb52dade0b7bd280339d2a1f3d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -67,18 +88,18 @@
"flake": false, "flake": false,
"locked": { "locked": {
"host": "gitlab.freedesktop.org", "host": "gitlab.freedesktop.org",
"lastModified": 1695277534, "lastModified": 1703963193,
"narHash": "sha256-LEIUGXvKR5DYFQUTavC3yifcObvG4XZUUHfxXmu8nEM=", "narHash": "sha256-ke8drv6PTrdQDruWbajrRJffP9A9PU6FRyjJGNZRTs4=",
"owner": "wlroots", "owner": "wlroots",
"repo": "wlroots", "repo": "wlroots",
"rev": "98a745d926d8048bc30aef11b421df207a01c279", "rev": "f81c3d93cd6f61b20ae784297679283438def8df",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
"host": "gitlab.freedesktop.org", "host": "gitlab.freedesktop.org",
"owner": "wlroots", "owner": "wlroots",
"repo": "wlroots", "repo": "wlroots",
"rev": "98a745d926d8048bc30aef11b421df207a01c279", "rev": "f81c3d93cd6f61b20ae784297679283438def8df",
"type": "gitlab" "type": "gitlab"
} }
}, },
@ -87,6 +108,7 @@
"hyprland-protocols": [ "hyprland-protocols": [
"hyprland-protocols" "hyprland-protocols"
], ],
"hyprlang": "hyprlang",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
@ -95,11 +117,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1694628480, "lastModified": 1704659450,
"narHash": "sha256-Qg9hstRw0pvjGu5hStkr2UX1D73RYcQ9Ns/KnZMIm9w=", "narHash": "sha256-3lyoUVtUWz1LuxbltAtkJSK2IlVXmKhxCRU2/0PYCms=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland", "repo": "xdg-desktop-portal-hyprland",
"rev": "8f45a6435069b9e24ebd3160eda736d7a391cbf2", "rev": "6a5de92769d5b7038134044053f90e7458f6a197",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -12,7 +12,7 @@
host = "gitlab.freedesktop.org"; host = "gitlab.freedesktop.org";
owner = "wlroots"; owner = "wlroots";
repo = "wlroots"; repo = "wlroots";
rev = "98a745d926d8048bc30aef11b421df207a01c279"; rev = "f81c3d93cd6f61b20ae784297679283438def8df";
flake = false; flake = false;
}; };
@ -62,13 +62,16 @@
inherit inherit
(pkgsFor.${system}) (pkgsFor.${system})
# hyprland-packages # hyprland-packages
hyprland hyprland
hyprland-unwrapped hyprland-unwrapped
hyprland-debug hyprland-debug
hyprland-nvidia hyprland-legacy-renderer
# hyprland-extras # hyprland-extras
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
# dependencies # dependencies
hyprland-protocols hyprland-protocols
wlroots-hyprland wlroots-hyprland
udis86 udis86
@ -76,17 +79,18 @@
}); });
devShells = eachSystem (system: { devShells = eachSystem (system: {
default = pkgsFor.${system}.mkShell.override { default =
stdenv = pkgsFor.${system}.gcc13Stdenv; pkgsFor.${system}.mkShell.override {
} { stdenv = pkgsFor.${system}.gcc13Stdenv;
name = "hyprland-shell"; } {
nativeBuildInputs = with pkgsFor.${system}; [cmake python3]; name = "hyprland-shell";
buildInputs = [self.packages.${system}.wlroots-hyprland]; nativeBuildInputs = with pkgsFor.${system}; [cmake python3];
inputsFrom = [ buildInputs = [self.packages.${system}.wlroots-hyprland];
self.packages.${system}.wlroots-hyprland inputsFrom = [
self.packages.${system}.hyprland self.packages.${system}.wlroots-hyprland
]; self.packages.${system}.hyprland
}; ];
};
}); });
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);

8
hyprctl/CMakeLists.txt Normal file
View file

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.19)
project(
hyprctl
DESCRIPTION "Control utility for Hyprland"
)
add_executable(hyprctl "main.cpp")

View file

@ -1,4 +1,4 @@
all: all:
$(CXX) -std=c++2b ./main.cpp -o ./hyprctl $(CXX) $(CXXFLAGS) -std=c++2b ./main.cpp -o ./hyprctl
clean: clean:
rm ./hyprctl rm ./hyprctl

View file

@ -26,31 +26,35 @@
const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args] const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args]
commands: commands:
monitors
workspaces
activeworkspace
clients
activewindow activewindow
layers activeworkspace
devices
binds binds
clients
cursorpos
decorations
devices
dispatch dispatch
keyword getoption
version globalshortcuts
kill
splash
hyprpaper hyprpaper
instances
keyword
kill
layers
layouts
monitors
notify
plugin
reload reload
setcursor setcursor
getoption
cursorpos
switchxkblayout
seterror seterror
setprop setprop
plugin splash
notify switchxkblayout
globalshortcuts systeminfo
instances version
workspacerules
workspaces
flags: flags:
-j -> output in JSON -j -> output in JSON
@ -273,7 +277,6 @@ bool isNumber(const std::string& str, bool allowfloat) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
int bflag = 0, sflag = 0, index, c;
bool parseArgs = true; bool parseArgs = true;
if (argc < 2) { if (argc < 2) {
@ -287,7 +290,7 @@ int main(int argc, char** argv) {
bool json = false; bool json = false;
std::string overrideInstance = ""; std::string overrideInstance = "";
for (auto i = 0; i < ARGS.size(); ++i) { for (std::size_t i = 0; i < ARGS.size(); ++i) {
if (ARGS[i] == "--") { if (ARGS[i] == "--") {
// Stop parsing arguments after -- // Stop parsing arguments after --
parseArgs = false; parseArgs = false;
@ -329,6 +332,12 @@ int main(int argc, char** argv) {
fullRequest = fullArgs + "/" + fullRequest; fullRequest = fullArgs + "/" + fullRequest;
// instances is HIS-independent
if (fullRequest.contains("/instances")) {
instancesRequest(json);
return 0;
}
if (overrideInstance.contains("_")) if (overrideInstance.contains("_"))
instanceSignature = overrideInstance; instanceSignature = overrideInstance;
else if (!overrideInstance.empty()) { else if (!overrideInstance.empty()) {
@ -341,7 +350,7 @@ int main(int argc, char** argv) {
const auto INSTANCES = instances(); const auto INSTANCES = instances();
if (INSTANCENO < 0 || INSTANCENO >= INSTANCES.size()) { if (INSTANCENO < 0 || static_cast<std::size_t>(INSTANCENO) >= INSTANCES.size()) {
std::cout << "no such instance\n"; std::cout << "no such instance\n";
return 1; return 1;
} }
@ -370,6 +379,8 @@ int main(int argc, char** argv) {
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/activeworkspace")) else if (fullRequest.contains("/activeworkspace"))
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/workspacerules"))
request(fullRequest);
else if (fullRequest.contains("/activewindow")) else if (fullRequest.contains("/activewindow"))
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/layers")) else if (fullRequest.contains("/layers"))
@ -378,6 +389,8 @@ int main(int argc, char** argv) {
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/kill")) else if (fullRequest.contains("/kill"))
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/systeminfo"))
request(fullRequest);
else if (fullRequest.contains("/splash")) else if (fullRequest.contains("/splash"))
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/devices")) else if (fullRequest.contains("/devices"))
@ -394,8 +407,8 @@ int main(int argc, char** argv) {
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/globalshortcuts")) else if (fullRequest.contains("/globalshortcuts"))
request(fullRequest); request(fullRequest);
else if (fullRequest.contains("/instances")) else if (fullRequest.contains("/rollinglog"))
instancesRequest(json); request(fullRequest);
else if (fullRequest.contains("/switchxkblayout")) else if (fullRequest.contains("/switchxkblayout"))
request(fullRequest, 2); request(fullRequest, 2);
else if (fullRequest.contains("/seterror")) else if (fullRequest.contains("/seterror"))
@ -414,8 +427,12 @@ int main(int argc, char** argv) {
request(fullRequest, 1); request(fullRequest, 1);
else if (fullRequest.contains("/keyword")) else if (fullRequest.contains("/keyword"))
request(fullRequest, 2); request(fullRequest, 2);
else if (fullRequest.contains("/decorations"))
request(fullRequest, 1);
else if (fullRequest.contains("/hyprpaper")) else if (fullRequest.contains("/hyprpaper"))
requestHyprpaper(fullRequest); requestHyprpaper(fullRequest);
else if (fullRequest.contains("/layouts"))
request(fullRequest);
else if (fullRequest.contains("/--help")) else if (fullRequest.contains("/--help"))
printf("%s", USAGE.c_str()); printf("%s", USAGE.c_str());
else { else {

16
hyprpm/CMakeLists.txt Normal file
View file

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.19)
project(
hyprpm
DESCRIPTION "A Hyprland Plugin Manager"
)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23)
pkg_check_modules(tomlplusplus REQUIRED IMPORTED_TARGET tomlplusplus)
add_executable(hyprpm ${SRCFILES})
target_link_libraries(hyprpm PUBLIC PkgConfig::tomlplusplus)

View file

@ -0,0 +1,222 @@
#include "DataState.hpp"
#include <toml++/toml.hpp>
#include <iostream>
#include <filesystem>
#include <fstream>
#include "PluginManager.hpp"
std::string DataState::getDataStatePath() {
const auto HOME = getenv("HOME");
if (!HOME) {
std::cerr << "DataState: no $HOME\n";
throw std::runtime_error("no $HOME");
return "";
}
const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME");
if (XDG_DATA_HOME)
return std::string{XDG_DATA_HOME} + "/hyprpm";
return std::string{HOME} + "/.local/share/hyprpm";
}
void DataState::ensureStateStoreExists() {
const auto PATH = getDataStatePath();
if (!std::filesystem::exists(PATH))
std::filesystem::create_directories(PATH);
}
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
ensureStateStoreExists();
const auto PATH = getDataStatePath() + "/" + repo.name;
std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"repository", toml::table{
{"name", repo.name},
{"hash", repo.hash},
{"url", repo.url}
}}
};
for (auto& p : repo.plugins) {
// copy .so to the good place
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");
DATA.emplace(p.name, toml::table{
{"filename", p.name + ".so"},
{"enabled", p.enabled},
{"failed", p.failed}
});
}
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
bool DataState::pluginRepoExists(const std::string& urlOrName) {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName)
return true;
}
return false;
}
void DataState::removePluginRepo(const std::string& urlOrName) {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
if (URL == urlOrName || NAME == urlOrName) {
// unload the plugins!!
for (const auto& file : std::filesystem::directory_iterator(entry.path())) {
if (!file.path().string().ends_with(".so"))
continue;
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
}
std::filesystem::remove_all(entry.path());
return;
}
}
}
void DataState::updateGlobalState(const SGlobalState& state) {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"state", toml::table{
{"hash", state.headersHashCompiled},
{"dont_warn_install", state.dontWarnInstall}
}}
};
// clang-format on
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
ofs << DATA;
ofs.close();
}
SGlobalState DataState::getGlobalState() {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
if (!std::filesystem::exists(PATH + "/state.toml"))
return SGlobalState{};
auto DATA = toml::parse_file(PATH + "/state.toml");
SGlobalState state;
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
return state;
}
std::vector<SPluginRepository> DataState::getAllRepositories() {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
std::vector<SPluginRepository> repos;
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
const auto NAME = STATE["repository"]["name"].value_or("");
const auto URL = STATE["repository"]["url"].value_or("");
const auto HASH = STATE["repository"]["hash"].value_or("");
SPluginRepository repo;
repo.hash = HASH;
repo.name = NAME;
repo.url = URL;
for (const auto& [key, val] : STATE) {
if (key == "repository")
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, FAILED});
}
repos.push_back(repo);
}
return repos;
}
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
ensureStateStoreExists();
const auto PATH = getDataStatePath();
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
if (!entry.is_directory())
continue;
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
for (const auto& [key, val] : STATE) {
if (key == "repository")
continue;
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);
state << STATE;
state.close();
return true;
}
}
return false;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <vector>
#include "Plugin.hpp"
struct SGlobalState {
std::string headersHashCompiled = "";
bool dontWarnInstall = false;
};
namespace DataState {
std::string getDataStatePath();
void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo);
void removePluginRepo(const std::string& urlOrName);
bool pluginRepoExists(const std::string& urlOrName);
void updateGlobalState(const SGlobalState& state);
SGlobalState getGlobalState();
bool setPluginEnabled(const std::string& name, bool enabled);
std::vector<SPluginRepository> getAllRepositories();
};

View file

@ -0,0 +1,104 @@
#include "Manifest.hpp"
#include <toml++/toml.hpp>
#include <iostream>
CManifest::CManifest(const eManifestType type, const std::string& path) {
auto manifest = toml::parse_file(path);
if (type == MANIFEST_HYPRLOAD) {
for (auto& [key, val] : manifest) {
if (key.str().ends_with(".build"))
continue;
CManifest::SManifestPlugin plugin;
plugin.name = key;
m_vPlugins.push_back(plugin);
}
for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.version = manifest[plugin.name]["version"].value_or("?");
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
auto authors = manifest[plugin.name]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
plugin.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest[plugin.name]["author"].value_or("");
if (!std::string{author}.empty())
plugin.authors.push_back(author);
}
auto buildSteps = manifest[plugin.name]["build"]["steps"].as_array();
if (buildSteps) {
for (auto&& s : *buildSteps) {
plugin.buildSteps.push_back(s.as_string()->value_or("?"));
}
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
m_bGood = false;
return;
}
}
} else if (type == MANIFEST_HYPRPM) {
m_sRepository.name = manifest["repository"]["name"].value_or("");
auto authors = manifest["repository"]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
m_sRepository.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest["repository"]["author"].value_or("");
if (!std::string{author}.empty())
m_sRepository.authors.push_back(author);
}
auto pins = manifest["repository"]["commit_pins"].as_array();
if (pins) {
for (auto&& pin : *pins) {
auto pinArr = pin.as_array();
if (pinArr && pinArr->get(1))
m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
}
}
for (auto& [key, val] : manifest) {
if (key.str() == "repository")
continue;
CManifest::SManifestPlugin plugin;
plugin.name = key;
m_vPlugins.push_back(plugin);
}
for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.output = manifest[plugin.name]["output"].value_or("?");
auto authors = manifest[plugin.name]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
plugin.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest[plugin.name]["author"].value_or("");
if (!std::string{author}.empty())
plugin.authors.push_back(author);
}
auto buildSteps = manifest[plugin.name]["build"].as_array();
if (buildSteps) {
for (auto&& s : *buildSteps) {
plugin.buildSteps.push_back(s.as_string()->value_or("?"));
}
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
m_bGood = false;
return;
}
}
} else {
// ???
m_bGood = false;
}
}

View file

@ -0,0 +1,33 @@
#pragma once
#include <string>
#include <vector>
enum eManifestType {
MANIFEST_HYPRLOAD,
MANIFEST_HYPRPM
};
class CManifest {
public:
CManifest(const eManifestType type, const std::string& path);
struct SManifestPlugin {
std::string name;
std::string description;
std::string version;
std::vector<std::string> authors;
std::vector<std::string> buildSteps;
std::string output;
bool failed = false;
};
struct {
std::string name;
std::vector<std::string> authors;
std::vector<std::pair<std::string, std::string>> commitPins;
} m_sRepository;
std::vector<SManifestPlugin> m_vPlugins;
bool m_bGood = true;
};

View file

@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <vector>
struct SPlugin {
std::string name;
std::string filename;
bool enabled = false;
bool failed = false;
};
struct SPluginRepository {
std::string url;
std::string name;
std::vector<SPlugin> plugins;
std::string hash;
};

View file

@ -0,0 +1,731 @@
#include "PluginManager.hpp"
#include "../helpers/Colors.hpp"
#include "../progress/CProgressBar.hpp"
#include "Manifest.hpp"
#include "DataState.hpp"
#include <iostream>
#include <array>
#include <filesystem>
#include <thread>
#include <fstream>
#include <algorithm>
#include <toml++/toml.hpp>
static std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty())
return str;
int countBefore = 0;
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
countBefore++;
}
int countAfter = 0;
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
countAfter++;
}
str = str.substr(countBefore, str.length() - countBefore - countAfter);
return str;
}
static std::string execAndGet(std::string cmd) {
cmd += " 2>&1";
std::array<char, 128> buffer;
std::string result;
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe)
return "";
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
SHyprlandVersion CPluginManager::getHyprlandVersion() {
static SHyprlandVersion ver;
static bool once = false;
if (once)
return ver;
once = true;
const auto HLVERCALL = execAndGet("hyprctl version");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "version returned: " << HLVERCALL << "\n";
if (!HLVERCALL.contains("Tag:")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " You don't seem to be running Hyprland.";
return SHyprlandVersion{};
}
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << "\n";
ver = SHyprlandVersion{hlbranch, hlcommit};
return ver;
}
bool CPluginManager::addNewPluginRepo(const std::string& url) {
if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n";
return false;
}
auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) {
std::cout << Colors::YELLOW << "!" << Colors::RED << " Disclaimer:\n " << Colors::RESET
<< "plugins, especially not official, have no guarantee of stability, availablity or security.\n Run them at your own risk.\n "
<< "This message will not appear again.\n";
GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE);
}
std::cout << Colors::GREEN << "" << Colors::RESET << Colors::RED << " adding a new plugin repository " << Colors::RESET << "from " << url << "\n " << Colors::RED
<< "MAKE SURE" << Colors::RESET << " that you trust the authors. " << Colors::RED << "DO NOT" << Colors::RESET
<< " install random plugins without verifying the code and author.\n "
<< "Are you sure? [Y/n] ";
std::fflush(stdout);
std::string input;
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
return false;
}
CProgressBar progress;
progress.m_iMaxSteps = 5;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Cloning the plugin repository";
progress.print();
if (!std::filesystem::exists("/tmp/hyprpm")) {
std::filesystem::create_directory("/tmp/hyprpm");
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
if (std::filesystem::exists("/tmp/hyprpm/new")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old plugin repo build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/new");
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " new");
if (!std::filesystem::exists("/tmp/hyprpm/new")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. shell returned:\n" << ret << "\n";
return false;
}
progress.m_iSteps = 1;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.m_szCurrentMessage = "Reading the manifest";
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/new/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/new/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/new/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/new/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
return false;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
return false;
}
progress.m_iSteps = 2;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:");
for (auto& pl : pManifest->m_vPlugins) {
std::string message = std::string{Colors::RESET} + "" + pl.name + " by ";
for (auto& a : pl.authors) {
message += a + ", ";
}
if (pl.authors.size() > 0) {
message.pop_back();
message.pop_back();
}
message += " version " + pl.version;
progress.printMessageAbove(message);
}
progress.m_szCurrentMessage = "Verifying headers";
progress.print();
const auto HEADERSSTATUS = headersValid();
if (HEADERSSTATUS != HEADERS_OK) {
std::cerr << "\n" << headerError(HEADERSSTATUS);
return false;
}
progress.m_iSteps = 3;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " Hyprland headers OK");
progress.m_szCurrentMessage = "Building plugin(s)";
progress.print();
for (auto& p : pManifest->m_vPlugins) {
std::string out;
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
out += execAndGet("cd /tmp/hyprpm/new && " + bs) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/new/" + p.output)) {
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";
p.failed = true;
continue;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " all plugins built");
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing repository";
progress.print();
// add repo toml to DataState
SPluginRepository repo;
std::string repohash = execAndGet("cd /tmp/hyprpm/new/ && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
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, p.failed});
}
DataState::addNewPluginRepo(repo);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed repository");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " you can now enable the plugin(s) with hyprpm enable");
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/new");
return true;
}
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
if (!DataState::pluginRepoExists(urlOrName)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not remove the repository. Repository is not installed.\n";
return false;
}
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
<< "Are you sure? [Y/n] ";
std::fflush(stdout);
std::string input;
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
return false;
}
DataState::removePluginRepo(urlOrName);
return true;
}
eHeadersErrors CPluginManager::headersValid() {
const auto HLVER = getHyprlandVersion();
// find headers commit
auto headers = execAndGet("pkg-config --cflags --keep-system-cflags hyprland");
if (!headers.contains("-I/"))
return HEADERS_MISSING;
headers.pop_back(); // pop newline
std::string verHeader = "";
while (!headers.empty()) {
const auto PATH = headers.substr(0, headers.find(" -I/", 3));
if (headers.find(" -I/", 3) != std::string::npos)
headers = headers.substr(headers.find("-I/", 3));
else
headers = "";
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots"))
continue;
verHeader = removeBeginEndSpacesTabs(PATH.substr(2)) + "/hyprland/src/version.h";
break;
}
if (verHeader.empty())
return HEADERS_CORRUPTED;
// read header
std::ifstream ifs(verHeader);
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<char>(ifs)), (std::istreambuf_iterator<char>()));
ifs.close();
std::string hash = verHeaderContent.substr(verHeaderContent.find("#define GIT_COMMIT_HASH") + 23);
hash = hash.substr(0, hash.find_first_of('\n'));
hash = hash.substr(hash.find_first_of('"') + 1);
hash = hash.substr(0, hash.find_first_of('"'));
if (hash != HLVER.hash)
return HEADERS_MISMATCHED;
return HEADERS_OK;
}
bool CPluginManager::updateHeaders() {
const auto HLVER = getHyprlandVersion();
if (!std::filesystem::exists("/tmp/hyprpm")) {
std::filesystem::create_directory("/tmp/hyprpm");
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);
return true;
}
CProgressBar progress;
progress.m_iMaxSteps = 5;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Cloning the hyprland repository";
progress.print();
if (std::filesystem::exists("/tmp/hyprpm/hyprland")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old hyprland source files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
}
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/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";
return false;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.m_iSteps = 2;
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
ret =
execAndGet("cd /tmp/hyprpm/hyprland && git checkout " + HLVER.branch + " 2>&1 && git submodule update --init 2>&1 && git reset --hard --recurse-submodules " + HLVER.hash);
if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned: " + ret);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " checked out to running ver");
progress.m_iSteps = 3;
progress.m_szCurrentMessage = "Building Hyprland";
progress.print();
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");
// le hack. Wlroots has to generate its build/include
ret = execAndGet("cd /tmp/hyprpm/hyprland/subprojects/wlroots && meson setup -Drenderers=gles2 -Dexamples=false build");
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.");
ret = execAndGet("pkexec sh \"-c\" \"cd /tmp/hyprpm/hyprland && make installheaders\"");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "pkexec returned: " << ret << "\n";
// remove build files
std::filesystem::remove_all("/tmp/hyprpm/hyprland");
auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed headers");
progress.m_iSteps = 5;
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));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
std::cout << "\n";
std::cerr << "\n" << headerError(HEADERSVALID);
return false;
}
return true;
}
bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (headersValid() != HEADERS_OK) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
return false;
}
const auto REPOS = DataState::getAllRepositories();
if (REPOS.size() < 1) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " No repos to update.\n";
return true;
}
const auto HLVER = getHyprlandVersion();
CProgressBar progress;
progress.m_iMaxSteps = REPOS.size() * 2 + 1;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Updating repositories";
progress.print();
for (auto& repo : REPOS) {
bool update = forceUpdateAll;
progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating " + repo.name;
progress.print();
progress.printMessageAbove(std::string{Colors::RESET} + " → checking for updates for " + repo.name);
if (std::filesystem::exists("/tmp/hyprpm/update")) {
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " old update build files found in temp directory, removing.");
std::filesystem::remove_all("/tmp/hyprpm/update");
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + repo.url);
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " update");
if (!std::filesystem::exists("/tmp/hyprpm/update")) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not clone repo: shell returned:\n" + ret;
return false;
}
if (!update) {
// check if git has updates
std::string hash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
if (!hash.empty())
hash.pop_back();
update = update || hash != repo.hash;
}
if (!update) {
std::filesystem::remove_all("/tmp/hyprpm/update");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " is up-to-date.");
progress.m_iSteps++;
progress.print();
continue;
}
// we need to update
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " has updates.");
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + repo.name);
progress.m_iSteps++;
progress.print();
std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists("/tmp/hyprpm/update/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, "/tmp/hyprpm/update/hyprpm.toml");
} else if (std::filesystem::exists("/tmp/hyprpm/update/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest");
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, "/tmp/hyprpm/update/hyprload.toml");
}
if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n";
continue;
}
if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n";
continue;
}
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/update/ && git reset --hard --recurse-submodules " + plugin);
}
}
bool failed = false;
for (auto& p : pManifest->m_vPlugins) {
std::string out;
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
for (auto& bs : p.buildSteps) {
out += execAndGet("cd /tmp/hyprpm/update && " + bs) + "\n";
}
if (!std::filesystem::exists("/tmp/hyprpm/update/" + p.output)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n";
failed = true;
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n";
break;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output);
}
if (failed)
continue;
// add repo toml to DataState
SPluginRepository newrepo = repo;
newrepo.plugins.clear();
execAndGet(
"cd /tmp/hyprpm/update/ && git pull --recurse-submodules && git reset --hard --recurse-submodules"); // repo hash in the state.toml has to match head and not any pin
std::string repohash = execAndGet("cd /tmp/hyprpm/update && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
newrepo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) {
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
newrepo.plugins.push_back(SPlugin{p.name, "/tmp/hyprpm/update/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
}
DataState::removePluginRepo(newrepo.name);
DataState::addNewPluginRepo(newrepo);
std::filesystem::remove_all("/tmp/hyprpm/update");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
}
progress.m_iSteps++;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
return true;
}
bool CPluginManager::enablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, true);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Enabled " << name << "\n";
return ret;
}
bool CPluginManager::disablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, false);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Disabled " << name << "\n";
return ret;
}
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (headersValid() != HEADERS_OK) {
std::cerr << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
return LOADSTATE_HEADERS_OUTDATED;
}
const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) {
std::cerr << "PluginManager: no $HOME or HIS\n";
return LOADSTATE_FAIL;
}
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
auto pluginLines = execAndGet("hyprctl plugins list | grep Plugin");
std::vector<std::string> loadedPlugins;
std::cout << Colors::GREEN << "" << Colors::RESET << " Ensuring plugin load state\n";
// iterate line by line
while (!pluginLines.empty()) {
auto plLine = pluginLines.substr(0, pluginLines.find("\n"));
if (pluginLines.find("\n") != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find("\n") + 1);
else
pluginLines = "";
if (plLine.back() != ':')
continue;
plLine = plLine.substr(7);
plLine = plLine.substr(0, plLine.find(" by "));
loadedPlugins.push_back(plLine);
}
// get state
const auto REPOS = DataState::getAllRepositories();
auto enabled = [REPOS](const std::string& plugin) -> bool {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (p.name == plugin && p.enabled)
return true;
}
}
return false;
};
auto repoForName = [REPOS](const std::string& name) -> std::string {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (p.name == name)
return r.name;
}
}
return "";
};
// unload disabled plugins
for (auto& p : loadedPlugins) {
if (!enabled(p)) {
// unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::cout << Colors::GREEN << "" << Colors::RESET << " Unloaded " << p << "\n";
}
}
// load enabled plugins
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
if (!p.enabled)
continue;
if (std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
continue;
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
std::cout << Colors::GREEN << "" << Colors::RESET << " Loaded " << p.name << "\n";
}
}
std::cout << Colors::GREEN << "" << Colors::RESET << " Plugin load state ensured\n";
return LOADSTATE_OK;
}
bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
if (load)
execAndGet("hyprctl plugin load " + path);
else
execAndGet("hyprctl plugin unload " + path);
return true;
}
void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories();
for (auto& r : REPOS) {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n";
for (auto& p : r.plugins) {
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";
}
}
}
void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message) {
execAndGet("hyprctl notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
}
std::string CPluginManager::headerError(const eHeadersErrors err) {
switch (err) {
case HEADERS_CORRUPTED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers corrupted. Please run hyprpm update to fix those.\n";
case HEADERS_MISMATCHED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers version mismatch. Please run hyprpm update to fix those.\n";
case HEADERS_NOT_HYPRLAND: return std::string{Colors::RED} + "" + Colors::RESET + " It doesn't seem you are running on hyprland.\n";
case HEADERS_MISSING: return std::string{Colors::RED} + "" + Colors::RESET + " Headers missing. Please run hyprpm update to fix those.\n";
case HEADERS_DUPLICATED: {
return std::string{Colors::RED} + "" + Colors::RESET + " Headers duplicated!!! This is a very bad sign.\n" +
" This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" +
" If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n";
}
default: break;
}
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
}

View file

@ -0,0 +1,63 @@
#pragma once
#include <memory>
#include <string>
enum eHeadersErrors {
HEADERS_OK = 0,
HEADERS_NOT_HYPRLAND,
HEADERS_MISSING,
HEADERS_CORRUPTED,
HEADERS_MISMATCHED,
HEADERS_DUPLICATED
};
enum eNotifyIcons {
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
ICON_ERROR,
ICON_CONFUSED,
ICON_OK,
ICON_NONE
};
enum ePluginLoadStateReturn {
LOADSTATE_OK = 0,
LOADSTATE_FAIL,
LOADSTATE_PARTIAL_FAIL,
LOADSTATE_HEADERS_OUTDATED
};
struct SHyprlandVersion {
std::string branch;
std::string hash;
};
class CPluginManager {
public:
bool addNewPluginRepo(const std::string& url);
bool removePluginRepo(const std::string& urlOrName);
eHeadersErrors headersValid();
bool updateHeaders();
bool updatePlugins(bool forceUpdateAll);
void listAllPlugins();
bool enablePlugin(const std::string& name);
bool disablePlugin(const std::string& name);
ePluginLoadStateReturn ensurePluginsLoadState();
bool loadUnloadPlugin(const std::string& path, bool load);
SHyprlandVersion getHyprlandVersion();
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
bool m_bVerbose = false;
private:
std::string headerError(const eHeadersErrors err);
};
inline std::unique_ptr<CPluginManager> g_pPluginManager;

View file

@ -0,0 +1,11 @@
#pragma once
namespace Colors {
constexpr const char* RED = "\x1b[31m";
constexpr const char* GREEN = "\x1b[32m";
constexpr const char* YELLOW = "\x1b[33m";
constexpr const char* BLUE = "\x1b[34m";
constexpr const char* MAGENTA = "\x1b[35m";
constexpr const char* CYAN = "\x1b[36m";
constexpr const char* RESET = "\x1b[0m";
};

149
hyprpm/src/main.cpp Normal file
View file

@ -0,0 +1,149 @@
#include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp"
#include "core/PluginManager.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
add [url] Install a new plugin repository from git
remove [url/name] Remove an installed plugin repository
enable [name] Enable a plugin
disable [name] Disable a plugin
update Check and update all plugins if needed
reload Reload hyprpm state. Ensure all enabled plugins are loaded.
list List all installed plugins
Flags:
--notify | -n Send a hyprland notification for important events (e.g. load fail)
--help | -h Show this menu
--verbose | -v Enable too much logging
)#";
int main(int argc, char** argv, char** envp) {
std::vector<std::string> ARGS{argc};
for (int i = 0; i < argc; ++i) {
ARGS[i] = std::string{argv[i]};
}
if (ARGS.size() < 2) {
std::cout << HELP;
return 1;
}
std::vector<std::string> command;
bool notify = false, verbose = false;
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
if (ARGS[i] == "--help" || ARGS[i] == "-h") {
std::cout << HELP;
return 0;
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
} else {
std::cerr << "Unrecognized option " << ARGS[i];
return 1;
}
} else {
command.push_back(ARGS[i]);
}
}
if (command.empty()) {
std::cout << HELP;
return 0;
}
g_pPluginManager = std::make_unique<CPluginManager>();
g_pPluginManager->m_bVerbose = verbose;
if (command[0] == "add") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for add.\n";
return 1;
}
return g_pPluginManager->addNewPluginRepo(command[1]) ? 0 : 1;
} else if (command[0] == "remove") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for remove.\n";
return 1;
}
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();
if (headers) {
bool ret1 = g_pPluginManager->updatePlugins(!headersValid);
if (!ret1)
return 1;
auto ret2 = g_pPluginManager->ensurePluginsLoadState();
if (ret2 != LOADSTATE_OK)
return 1;
} else if (notify)
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
} else if (command[0] == "enable") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for enable.\n";
return 1;
}
if (!g_pPluginManager->enablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't enable plugin (missing?)\n";
return 1;
}
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "disable") {
if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for disable.\n";
return 1;
}
if (!g_pPluginManager->disablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't disable plugin (missing?)\n";
return 1;
}
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK && notify) {
switch (ret) {
case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
case LOADSTATE_HEADERS_OUTDATED:
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
break;
default: break;
}
} else if (notify) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}
} else if (command[0] == "list") {
g_pPluginManager->listAllPlugins();
} else {
std::cout << HELP;
return 1;
}
return 0;
}

10
hyprpm/src/meson.build Normal file
View file

@ -0,0 +1,10 @@
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n')
executable('hyprpm', src,
dependencies: [
dependency('threads'),
dependency('tomlplusplus')
],
install : true
)

View file

@ -0,0 +1,80 @@
#include "CProgressBar.hpp"
#include <iostream>
#include <algorithm>
#include <cmath>
#include <format>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#include "../helpers/Colors.hpp"
void CProgressBar::printMessageAbove(const std::string& msg) {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
std::string spaces;
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r" << msg << "\n";
print();
}
void CProgressBar::print() {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (m_bFirstPrint)
std::cout << "\n";
m_bFirstPrint = false;
std::string spaces;
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r";
std::string message = "";
float percentDone = 0;
if (m_fPercentage >= 0)
percentDone = m_fPercentage;
else
percentDone = (float)m_iSteps / (float)m_iMaxSteps;
const auto BARWIDTH = std::clamp(w.ws_col - static_cast<unsigned long>(m_szCurrentMessage.length()) - 2, 0UL, 50UL);
// draw bar
message += std::string{" "} + Colors::GREEN;
size_t i = 0;
for (; i < std::floor(percentDone * BARWIDTH); ++i) {
message += "";
}
if (i < BARWIDTH) {
i++;
message += std::string{""} + Colors::RESET;
for (; i < BARWIDTH; ++i) {
message += "";
}
} else
message += Colors::RESET;
// draw progress
if (m_fPercentage >= 0)
message += " " + std::format("{}%", static_cast<int>(percentDone * 100.0)) + " ";
else
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
// draw message
std::cout << message + " " + m_szCurrentMessage;
std::fflush(stdout);
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <string>
class CProgressBar {
public:
void print();
void printMessageAbove(const std::string& msg);
std::string m_szCurrentMessage = "";
size_t m_iSteps = 0;
size_t m_iMaxSteps = 0;
float m_fPercentage = -1; // if != -1, use percentage
private:
bool m_bFirstPrint = true;
};

View file

@ -20,25 +20,19 @@ else
error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)') error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)')
endif endif
GIT_BRANCH = run_command('git', 'rev-parse', '--abbrev-ref', 'HEAD', check: false).stdout().strip()
GIT_COMMIT_HASH = run_command('git', 'rev-parse', 'HEAD', check: false).stdout().strip()
GIT_COMMIT_MESSAGE = run_command('sh', '-c', 'git show | head -n 5 | tail -n 1', check: false).stdout().strip()
GIT_DIRTY = run_command('sh', '-c', 'git diff-index --quiet HEAD -- || echo "dirty"', check: false).stdout().strip()
add_project_arguments( add_project_arguments(
[ [
'-Wno-unused-parameter', '-Wno-unused-parameter',
'-Wno-unused-value', '-Wno-unused-value',
'-Wno-missing-field-initializers', '-Wno-missing-field-initializers',
'-Wno-narrowing', '-Wno-narrowing',
f'-DGIT_BRANCH="@GIT_BRANCH@"',
f'-DGIT_COMMIT_HASH="@GIT_COMMIT_HASH@"',
f'-DGIT_COMMIT_MESSAGE="@GIT_COMMIT_MESSAGE@"',
f'-DGIT_DIRTY="@GIT_DIRTY@"',
], ],
language: 'cpp') language: 'cpp')
if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2']) wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
have_xwlr = wlroots.get_variable('features').get('xwayland') have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland')) xcb_dep = dependency('xcb', required: get_option('xwayland'))
@ -75,6 +69,8 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp') add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif endif
version_h = run_command('sh', '-c', 'scripts/generateVersion.sh')
globber = run_command('find', 'src', '-name', '*.h*', check: true) globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n') headers = globber.stdout().strip().split('\n')
foreach file : headers foreach file : headers
@ -84,6 +80,7 @@ endforeach
subdir('protocols') subdir('protocols')
subdir('src') subdir('src')
subdir('hyprctl') subdir('hyprctl')
subdir('hyprpm/src')
subdir('assets') subdir('assets')
subdir('example') subdir('example')
subdir('docs') subdir('docs')

View file

@ -10,6 +10,7 @@
git, git,
hyprland-protocols, hyprland-protocols,
jq, jq,
libGL,
libdrm, libdrm,
libinput, libinput,
libxcb, libxcb,
@ -18,6 +19,7 @@
pango, pango,
pciutils, pciutils,
systemd, systemd,
tomlplusplus,
udis86, udis86,
wayland, wayland,
wayland-protocols, wayland-protocols,
@ -26,21 +28,23 @@
xcbutilwm, xcbutilwm,
xwayland, xwayland,
debug ? false, debug ? false,
enableNvidiaPatches ? false,
enableXWayland ? true, enableXWayland ? true,
legacyRenderer ? false, legacyRenderer ? false,
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd, withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
wrapRuntimeDeps ? true, wrapRuntimeDeps ? true,
version ? "git", version ? "git",
commit, commit,
date,
# deprecated flags # deprecated flags
enableNvidiaPatches ? false,
nvidiaPatches ? false, nvidiaPatches ? false,
hidpiXWayland ? false, hidpiXWayland ? false,
}: }:
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been renamed `enableNvidiaPatches`"; assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert lib.assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland"; assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}${lib.optionalString debug "-debug"}"; pname = "hyprland${lib.optionalString debug "-debug"}";
inherit version; inherit version;
src = lib.cleanSourceWith { src = lib.cleanSourceWith {
@ -68,19 +72,21 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
buildInputs = buildInputs =
[ [
git
cairo cairo
git
hyprland-protocols hyprland-protocols
libdrm libdrm
libGL
libinput libinput
libxkbcommon libxkbcommon
mesa mesa
pango pango
pciutils
tomlplusplus
udis86 udis86
wayland wayland
wayland-protocols wayland-protocols
pciutils wlroots
(wlroots.override {inherit enableNvidiaPatches;})
] ]
++ lib.optionals enableXWayland [libxcb xcbutilwm xwayland] ++ lib.optionals enableXWayland [libxcb xcbutilwm xwayland]
++ lib.optionals withSystemd [systemd]; ++ lib.optionals withSystemd [systemd];
@ -90,8 +96,9 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
then "debug" then "debug"
else "release"; else "release";
mesonAutoFeatures = "disabled";
mesonFlags = builtins.concatLists [ mesonFlags = builtins.concatLists [
["-Dauto_features=disabled"]
(lib.optional enableXWayland "-Dxwayland=enabled") (lib.optional enableXWayland "-Dxwayland=enabled")
(lib.optional legacyRenderer "-Dlegacy_renderer=enabled") (lib.optional legacyRenderer "-Dlegacy_renderer=enabled")
(lib.optional withSystemd "-Dsystemd=enabled") (lib.optional withSystemd "-Dsystemd=enabled")
@ -105,9 +112,16 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
postPatch = '' postPatch = ''
# Fix hardcoded paths to /usr installation # Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp sed -i "s#/usr#$out#" src/render/OpenGL.cpp
substituteInPlace meson.build \
--replace "@GIT_COMMIT_HASH@" '${commit}' \ # Generate version.h
--replace "@GIT_DIRTY@" '${ cp src/version.h.in src/version.h
substituteInPlace src/version.h \
--replace "@HASH@" '${commit}' \
--replace "@BRANCH@" "" \
--replace "@MESSAGE@" "" \
--replace "@DATE@" "${date}" \
--replace "@TAG@" "" \
--replace "@DIRTY@" '${
if commit == "" if commit == ""
then "dirty" then "dirty"
else "" else ""
@ -118,7 +132,11 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
ln -s ${wlroots}/include/wlr $dev/include/hyprland/wlroots ln -s ${wlroots}/include/wlr $dev/include/hyprland/wlroots
${lib.optionalString wrapRuntimeDeps '' ${lib.optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \ wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [binutils pciutils]} --suffix PATH : ${lib.makeBinPath [
stdenv.cc
binutils
pciutils
]}
''} ''}
''; '';

View file

@ -4,174 +4,11 @@ self: {
pkgs, pkgs,
... ...
}: let }: let
cfg = config.wayland.windowManager.hyprland; inherit (pkgs.stdenv.hostPlatform) system;
defaultHyprlandPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable; package = self.packages.${system}.default;
inherit (cfg) enableNvidiaPatches;
};
in { in {
disabledModules = ["services/window-managers/hyprland.nix"]; config = {
wayland.windowManager.hyprland.package = lib.mkDefault package;
meta.maintainers = [lib.maintainers.fufexan];
options.wayland.windowManager.hyprland = {
enable =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to enable Hyprland, the dynamic tiling Wayland compositor
that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on
a TTY.
See <https://wiki.hyprland.org> for more information.
'';
};
package = lib.mkOption {
type = with lib.types; nullOr package;
default = defaultHyprlandPackage;
defaultText = lib.literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.wayland.windowManager.hyprland.xwayland.enable;
inherit (config.wayland.windowManager.hyprland) enableNvidiaPatches;
}
'';
description = lib.mdDoc ''
Hyprland package to use. Will override the 'xwayland' and
'enableNvidiaPatches' options.
Defaults to the one provided by the flake. Set it to
{package}`pkgs.hyprland` to use the one provided by nixpkgs or
if you have an overlay.
Set to null to not add any Hyprland package to your path. This should
be done if you want to use the NixOS module to install Hyprland.
'';
};
plugins = lib.mkOption {
type = with lib.types; listOf (either package path);
default = [];
description = lib.mdDoc ''
List of Hyprland plugins to use. Can either be packages or
absolute plugin paths.
'';
};
systemdIntegration = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.isLinux;
description = lib.mdDoc ''
Whether to enable {file}`hyprland-session.target` on
Hyprland startup. This links to {file}`graphical-session.target`.
Some important environment variables will be imported to systemd
and dbus user environment before reaching the target, including
- {env}`DISPLAY`
- {env}`HYPRLAND_INSTANCE_SIGNATURE`
- {env}`WAYLAND_DISPLAY`
- {env}`XDG_CURRENT_DESKTOP`
'';
};
disableAutoreload =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to disable automatically reloading Hyprland's configuration when
rebuilding the Home Manager profile.
'';
};
xwayland.enable = lib.mkEnableOption (lib.mdDoc "XWayland") // {default = true;};
enableNvidiaPatches = lib.mkEnableOption (lib.mdDoc "patching wlroots for better Nvidia support.");
extraConfig = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
default = "";
description = lib.mdDoc ''
Extra configuration lines to add to {file}`~/.config/hypr/hyprland.conf`.
'';
};
recommendedEnvironment =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to set the recommended environment variables.
'';
};
}; };
config = lib.mkIf cfg.enable {
warnings =
if (cfg.systemdIntegration || cfg.plugins != []) && cfg.extraConfig == null
then [
''
You have enabled hyprland.systemdIntegration or listed plugins in hyprland.plugins.
Your Hyprland config will be linked by home manager.
Set hyprland.extraConfig or unset hyprland.systemdIntegration and hyprland.plugins to remove this warning.
''
]
else [];
home.packages =
lib.optional (cfg.package != null) cfg.package
++ lib.optional cfg.xwayland.enable pkgs.xwayland;
home.sessionVariables =
lib.mkIf cfg.recommendedEnvironment {NIXOS_OZONE_WL = "1";};
xdg.configFile."hypr/hyprland.conf" = lib.mkIf (cfg.systemdIntegration || cfg.extraConfig != null || cfg.plugins != []) {
text =
(lib.optionalString cfg.systemdIntegration ''
exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && systemctl --user start hyprland-session.target
'')
+ lib.concatStrings (builtins.map (entry: let
plugin =
if lib.types.package.check entry
then "${entry}/lib/lib${entry.pname}.so"
else entry;
in "plugin = ${plugin}\n")
cfg.plugins)
+ (
if cfg.extraConfig != null
then cfg.extraConfig
else ""
);
onChange = let
hyprlandPackage =
if cfg.package == null
then defaultHyprlandPackage
else cfg.package;
in
lib.mkIf (!cfg.disableAutoreload) ''
( # execute in subshell so that `shopt` won't affect other scripts
shopt -s nullglob # so that nothing is done if /tmp/hypr/ does not exist or is empty
for instance in /tmp/hypr/*; do
HYPRLAND_INSTANCE_SIGNATURE=''${instance##*/} ${hyprlandPackage}/bin/hyprctl reload config-only \
|| true # ignore dead instance(s)
done
)
'';
};
systemd.user.targets.hyprland-session = lib.mkIf cfg.systemdIntegration {
Unit = {
Description = "Hyprland compositor session";
Documentation = ["man:systemd.special(7)"];
BindsTo = ["graphical-session.target"];
Wants = ["graphical-session-pre.target"];
After = ["graphical-session-pre.target"];
};
};
};
imports = [
(lib.mkRemovedOptionModule ["wayland" "windowManager" "hyprland" "xwayland" "hidpi"]
"Support for this option has been removed. Refer to https://wiki.hyprland.org/Configuring/XWayland for more info")
];
} }

View file

@ -2,98 +2,20 @@ inputs: {
config, config,
lib, lib,
pkgs, pkgs,
options,
... ...
}: }: let
with lib; let
cfg = config.programs.hyprland;
inherit (pkgs.stdenv.hostPlatform) system; inherit (pkgs.stdenv.hostPlatform) system;
cfg = config.programs.hyprland;
finalPortalPackage = cfg.portalPackage.override { package = inputs.self.packages.${system}.hyprland;
portalPackage = inputs.self.packages.${system}.xdg-desktop-portal-hyprland.override {
hyprland = cfg.finalPackage; hyprland = cfg.finalPackage;
}; };
in { in {
# disables Nixpkgs Hyprland module to avoid conflicts config = {
disabledModules = ["programs/hyprland.nix"]; programs.hyprland = {
package = lib.mkDefault package;
options.programs.hyprland = { portalPackage = lib.mkDefault portalPackage;
enable =
mkEnableOption null
// {
description = mdDoc ''
Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`.
See <https://wiki.hyprland.org> for more information.
'';
};
package = mkPackageOptionMD inputs.self.packages.${system} "hyprland" { };
finalPackage = mkOption {
type = types.package;
readOnly = true;
default = cfg.package.override {
enableXWayland = cfg.xwayland.enable;
enableNvidiaPatches = cfg.enableNvidiaPatches;
};
defaultText =
literalExpression
"`programs.hyprland.package` with applied configuration";
description = mdDoc ''
The Hyprland package after applying configuration.
'';
};
portalPackage = mkPackageOptionMD inputs.xdph.packages.${system} "xdg-desktop-portal-hyprland" {};
xwayland.enable = mkEnableOption (mdDoc "support for XWayland") // {default = true;};
enableNvidiaPatches =
mkEnableOption null
// {
description = mdDoc "Whether to apply patches to wlroots for better Nvidia support.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [cfg.finalPackage];
# NixOS changed the name of this attribute between NixOS 23.05 and
# 23.11
fonts = if builtins.hasAttr "enableDefaultPackages" options.fonts
then {enableDefaultPackages = mkDefault true;}
else {enableDefaultFonts = mkDefault true;};
hardware.opengl.enable = mkDefault true;
programs = {
dconf.enable = mkDefault true;
xwayland.enable = mkDefault cfg.xwayland.enable;
};
security.polkit.enable = true;
services.xserver.displayManager.sessionPackages = [cfg.finalPackage];
xdg.portal = {
enable = mkDefault true;
extraPortals = [finalPortalPackage];
}; };
}; };
imports = with lib; [
(
mkRemovedOptionModule
["programs" "hyprland" "xwayland" "hidpi"]
"XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland"
)
(
mkRenamedOptionModule
["programs" "hyprland" "nvidiaPatches"]
["programs" "hyprland" "enableNvidiaPatches"]
)
];
} }

View file

@ -28,17 +28,26 @@ in {
self.overlays.wlroots-hyprland self.overlays.wlroots-hyprland
self.overlays.udis86 self.overlays.udis86
# Hyprland packages themselves # Hyprland packages themselves
(final: prev: { (final: prev: let
date = mkDate (self.lastModifiedDate or "19700101");
in {
hyprland = final.callPackage ./default.nix { hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv; stdenv = final.gcc13Stdenv;
version = "${props.version}+date=${mkDate (self.lastModifiedDate or "19700101")}_${self.shortRev or "dirty"}"; version = "${props.version}+date=${date}_${self.shortRev or "dirty"}";
wlroots = final.wlroots-hyprland; wlroots = final.wlroots-hyprland;
commit = self.rev or ""; commit = self.rev or "";
inherit (final) udis86 hyprland-protocols; inherit (final) udis86 hyprland-protocols;
inherit date;
}; };
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
hyprland-debug = final.hyprland.override {debug = true;}; hyprland-debug = final.hyprland.override {debug = true;};
hyprland-nvidia = final.hyprland.override {enableNvidiaPatches = true;}; hyprland-legacy-renderer = final.hyprland.override {legacyRenderer = true;};
hyprland-nvidia =
builtins.trace ''
hyprland-nvidia was removed. Please use the hyprland package.
Nvidia patches are no longer needed.
''
final.hyprland;
hyprland-hidpi = hyprland-hidpi =
builtins.trace '' builtins.trace ''
hyprland-hidpi was removed. Please use the hyprland package. hyprland-hidpi was removed. Please use the hyprland package.
@ -65,30 +74,6 @@ in {
wlroots-hyprland = final.callPackage ./wlroots.nix { wlroots-hyprland = final.callPackage ./wlroots.nix {
version = "${mkDate (inputs.wlroots.lastModifiedDate or "19700101")}_${inputs.wlroots.shortRev or "dirty"}"; version = "${mkDate (inputs.wlroots.lastModifiedDate or "19700101")}_${inputs.wlroots.shortRev or "dirty"}";
src = inputs.wlroots; src = inputs.wlroots;
libdisplay-info = prev.libdisplay-info.overrideAttrs (old: {
version = "0.1.1+date=2023-03-02";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "147d6611a64a6ab04611b923e30efacaca6fc678";
sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4=";
};
});
libliftoff = prev.libliftoff.overrideAttrs (old: {
version = "0.5.0-dev";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "d98ae243280074b0ba44bff92215ae8d785658c0";
sha256 = "sha256-DjwlS8rXE7srs7A8+tHqXyUsFGtucYSeq6X0T/pVOc8=";
};
NIX_CFLAGS_COMPILE = toString ["-Wno-error=sign-conversion"];
});
}; };
}; };
} }

View file

@ -1,23 +1,11 @@
diff --git a/meson.build b/meson.build diff --git a/meson.build b/meson.build
index f3802553..6a924a79 100644 index 1d2c7f9f..c5ef4e67 100644
--- a/meson.build --- a/meson.build
+++ b/meson.build +++ b/meson.build
@@ -21,9 +21,9 @@ else @@ -33,20 +33,7 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif endif
GIT_BRANCH = run_command('git', 'rev-parse', '--abbrev-ref', 'HEAD', check: false).stdout().strip()
-GIT_COMMIT_HASH = run_command('git', 'rev-parse', 'HEAD', check: false).stdout().strip()
+GIT_COMMIT_HASH = '@GIT_COMMIT_HASH@'
GIT_COMMIT_MESSAGE = run_command('sh', '-c', 'git show | head -n 5 | tail -n 1', check: false).stdout().strip()
-GIT_DIRTY = run_command('sh', '-c', 'git diff-index --quiet HEAD -- || echo "dirty"', check: false).stdout().strip()
+GIT_DIRTY = '@GIT_DIRTY@'
add_project_arguments(
[
@@ -39,21 +39,8 @@ add_project_arguments(
],
language: 'cpp')
-wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2']) -wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2'])
-have_xwlr = wlroots.get_variable('features').get('xwayland') -have_xwlr = wlroots.get_variable('features').get('xwayland')
-xcb_dep = dependency('xcb', required: get_option('xwayland')) -xcb_dep = dependency('xcb', required: get_option('xwayland'))
@ -32,17 +20,24 @@ index f3802553..6a924a79 100644
-have_xwayland = xcb_dep.found() and have_xwlr -have_xwayland = xcb_dep.found() and have_xwlr
- -
-if not have_xwayland -if not have_xwayland
- add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
+if get_option('xwayland').disabled() +if get_option('xwayland').disabled()
+ add_project_arguments('-DNO_XWAYLAND', language: 'cpp') add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false) @@ -69,8 +56,6 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
-version_h = run_command('sh', '-c', 'scripts/generateVersion.sh')
-
globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
diff --git a/src/meson.build b/src/meson.build diff --git a/src/meson.build b/src/meson.build
index 7b658d31..60aa4057 100644 index 0af864b9..38723b8c 100644
--- a/src/meson.build --- a/src/meson.build
+++ b/src/meson.build +++ b/src/meson.build
@@ -7,16 +7,16 @@ executable('Hyprland', src, @@ -9,16 +9,16 @@ executable('Hyprland', src,
server_protos, server_protos,
dependency('wayland-server'), dependency('wayland-server'),
dependency('wayland-client'), dependency('wayland-client'),

View file

@ -1,41 +0,0 @@
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index 9fe934f7..9662d4ee 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -176,7 +176,7 @@ static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer,
assert(wlr_egl_is_current(renderer->egl));
push_gles2_debug(renderer);
- glFlush();
+ glFinish();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
pop_gles2_debug(renderer);
diff --git a/types/output/render.c b/types/output/render.c
index 2e38919a..97f78608 100644
--- a/types/output/render.c
+++ b/types/output/render.c
@@ -240,22 +240,7 @@ bool output_pick_format(struct wlr_output *output,
}
uint32_t wlr_output_preferred_read_format(struct wlr_output *output) {
- struct wlr_renderer *renderer = output->renderer;
- assert(renderer != NULL);
-
- if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) {
- return DRM_FORMAT_INVALID;
- }
-
- if (!wlr_output_attach_render(output, NULL)) {
- return false;
- }
-
- uint32_t fmt = renderer->impl->preferred_read_format(renderer);
-
- output_clear_back_buffer(output);
-
- return fmt;
+ return DRM_FORMAT_XRGB8888;
}
struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output,

View file

@ -1,28 +1,13 @@
{ {
lib,
version, version,
src, src,
wlroots, wlroots,
hwdata,
libdisplay-info,
libliftoff,
enableXWayland ? true, enableXWayland ? true,
enableNvidiaPatches ? false,
}: }:
wlroots.overrideAttrs (old: { wlroots.overrideAttrs (old: {
inherit version src enableXWayland; inherit version src enableXWayland;
pname = "${old.pname}-hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}"; pname = "${old.pname}-hyprland";
patches = patches = [ ]; # don't inherit old.patches
(old.patches or [])
++ (lib.optionals enableNvidiaPatches [
./patches/wlroots-nvidia.patch
]);
buildInputs = old.buildInputs ++ [hwdata libliftoff libdisplay-info];
NIX_CFLAGS_COMPILE = toString [
"-Wno-error=maybe-uninitialized"
];
}) })

View file

@ -1,3 +1,3 @@
{ {
"version": "0.30.0" "version": "0.34.0"
} }

View file

@ -25,6 +25,7 @@ protocols = [
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'], [wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'], ['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'], ['wlr-output-power-management-unstable-v1.xml'],

16
scripts/generateVersion.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
cp -fr ./src/version.h.in ./src/version.h
HASH=$(git rev-parse HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
MESSAGE=$(git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g')
DATE=$(git show ${GIT_COMMIT_HASH} --no-patch --format=%cd --date=local)
DIRTY=$(git diff-index --quiet HEAD -- || echo dirty)
TAG=$(git describe --tags)
sed -i -e "s#@HASH@#${HASH}#" ./src/version.h
sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h
sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h
sed -i -e "s#@DATE@#${DATE}#" ./src/version.h
sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h
sed -i -e "s#@TAG@#${TAG}#" ./src/version.h

File diff suppressed because it is too large Load diff

View file

@ -27,9 +27,9 @@
#include "render/OpenGL.hpp" #include "render/OpenGL.hpp"
#include "hyprerror/HyprError.hpp" #include "hyprerror/HyprError.hpp"
#include "plugins/PluginSystem.hpp" #include "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp"
enum eManagersInitStage enum eManagersInitStage {
{
STAGE_PRIORITY = 0, STAGE_PRIORITY = 0,
STAGE_LATE STAGE_LATE
}; };
@ -53,7 +53,6 @@ class CCompositor {
wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr; wlr_drm_lease_v1_manager* m_sWRLDRMLeaseMgr;
wlr_xdg_activation_v1* m_sWLRXDGActivation; wlr_xdg_activation_v1* m_sWLRXDGActivation;
wlr_output_layout* m_sWLROutputLayout; wlr_output_layout* m_sWLROutputLayout;
wlr_idle* m_sWLRIdle;
wlr_idle_notifier_v1* m_sWLRIdleNotifier; wlr_idle_notifier_v1* m_sWLRIdleNotifier;
wlr_layer_shell_v1* m_sWLRLayerShell; wlr_layer_shell_v1* m_sWLRLayerShell;
wlr_xdg_shell* m_sWLRXDGShell; wlr_xdg_shell* m_sWLRXDGShell;
@ -62,7 +61,6 @@ class CCompositor {
wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr; wlr_virtual_keyboard_manager_v1* m_sWLRVKeyboardMgr;
wlr_output_manager_v1* m_sWLROutputMgr; wlr_output_manager_v1* m_sWLROutputMgr;
wlr_presentation* m_sWLRPresentation; wlr_presentation* m_sWLRPresentation;
wlr_input_inhibit_manager* m_sWLRInhibitMgr;
wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr; wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
wlr_egl* m_sWLREGL; wlr_egl* m_sWLREGL;
int m_iDRMFD; int m_iDRMFD;
@ -85,6 +83,7 @@ class CCompositor {
wlr_session_lock_manager_v1* m_sWLRSessionLockMgr; wlr_session_lock_manager_v1* m_sWLRSessionLockMgr;
wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr; wlr_gamma_control_manager_v1* m_sWLRGammaCtrlMgr;
wlr_cursor_shape_manager_v1* m_sWLRCursorShapeMgr; wlr_cursor_shape_manager_v1* m_sWLRCursorShapeMgr;
wlr_tearing_control_manager_v1* m_sWLRTearingControlMgr;
// ------------------------------------------------- // // ------------------------------------------------- //
std::string m_szWLDisplaySocket = ""; std::string m_szWLDisplaySocket = "";
@ -120,7 +119,8 @@ class CCompositor {
bool m_bSessionActive = true; bool m_bSessionActive = true;
bool m_bDPMSStateON = true; bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors. bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
wlr_output* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state bool m_bNextIsUnsafe = false; // because wlroots
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bIsShuttingDown = false; bool m_bIsShuttingDown = false;
// ------------------------------------------------- // // ------------------------------------------------- //
@ -135,10 +135,10 @@ class CCompositor {
void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr); void focusSurface(wlr_surface*, CWindow* pWindowOwner = nullptr);
bool windowExists(CWindow*); bool windowExists(CWindow*);
bool windowValidMapped(CWindow*); bool windowValidMapped(CWindow*);
CWindow* vectorToWindow(const Vector2D&); CWindow* vectorToWindowIdeal(const Vector2D&, CWindow* pIgnoreWindow = nullptr); // used only for finding a window to focus on, basically a "findFocusableWindow"
CWindow* vectorToWindowIdeal(const Vector2D&); // used only for finding a window to focus on, basically a "findFocusableWindow"
CWindow* vectorToWindowTiled(const Vector2D&); CWindow* vectorToWindowTiled(const Vector2D&);
wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**); wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector<std::unique_ptr<SLayerSurface>>*, Vector2D*, SLayerSurface**);
SIMEPopup* vectorToIMEPopup(const Vector2D& pos, std::list<SIMEPopup>& popups);
wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl); wlr_surface* vectorWindowToSurface(const Vector2D&, CWindow*, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*); Vector2D vectorToSurfaceLocal(const Vector2D&, CWindow*, wlr_surface*);
CWindow* windowFromCursor(); CWindow* windowFromCursor();
@ -165,16 +165,18 @@ class CCompositor {
void changeWindowZOrder(CWindow*, bool); void changeWindowZOrder(CWindow*, bool);
void cleanupFadingOut(const int& monid); void cleanupFadingOut(const int& monid);
CWindow* getWindowInDirection(CWindow*, char); CWindow* getWindowInDirection(CWindow*, char);
CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false); CWindow* getNextWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false); CWindow* getPrevWindowOnWorkspace(CWindow*, bool focusableOnly = false, std::optional<bool> floating = {});
int getNextAvailableNamedWorkspace(); int getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&); bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CWindow* getConstraintWindow(SMouse*); CWindow* getConstraintWindow(SMouse*);
CMonitor* getMonitorInDirection(const char&); CMonitor* getMonitorInDirection(const char&);
void updateAllWindowsAnimatedDecorationValues(); void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const int64_t& id);
void updateWindowAnimatedDecorationValues(CWindow*); void updateWindowAnimatedDecorationValues(CWindow*);
int getNextAvailableMonitorID(std::string const& name); int getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(CWorkspace*, CMonitor*); void moveWorkspaceToMonitor(CWorkspace*, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*); void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&); CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&); bool workspaceIDOutOfBounds(const int64_t&);
@ -191,7 +193,6 @@ class CCompositor {
void closeWindow(CWindow*); void closeWindow(CWindow*);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&); void forceReportSizesToWindowsOnWorkspace(const int&);
bool cursorOnReservedArea();
CWorkspace* createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused! CWorkspace* createNewWorkspace(const int&, const int&, const std::string& name = ""); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const int&, const std::string& name = ""); void renameWorkspace(const int&, const std::string& name = "");
void setActiveMonitor(CMonitor*); void setActiveMonitor(CMonitor*);
@ -205,6 +206,9 @@ class CCompositor {
void arrangeMonitors(); void arrangeMonitors();
void enterUnsafeState(); void enterUnsafeState();
void leaveUnsafeState(); void leaveUnsafeState();
void setPreferredScaleForSurface(wlr_surface* pSurface, double scale);
void setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform);
void updateSuspendedStates();
std::string explicitConfigPath; std::string explicitConfigPath;
@ -212,6 +216,7 @@ class CCompositor {
void initAllSignals(); void initAllSignals();
void setRandomSplash(); void setRandomSplash();
void initManagers(eManagersInitStage stage); void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
uint64_t m_iHyprlandPID = 0; uint64_t m_iHyprlandPID = 0;
}; };

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
enum eIcons #include "helpers/Vector2D.hpp"
{
enum eIcons {
ICON_WARNING = 0, ICON_WARNING = 0,
ICON_INFO, ICON_INFO,
ICON_HINT, ICON_HINT,
@ -11,8 +12,7 @@ enum eIcons
ICON_NONE ICON_NONE
}; };
enum eRenderStage enum eRenderStage {
{
RENDER_PRE = 0, /* Before binding the gl context */ RENDER_PRE = 0, /* Before binding the gl context */
RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */ RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */
RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */ RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */
@ -22,4 +22,34 @@ enum eRenderStage
RENDER_POST_MIRROR, /* After rendering a mirror */ RENDER_POST_MIRROR, /* After rendering a mirror */
RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */ RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */
RENDER_POST_WINDOW, /* After rendering a window (any pass) */ RENDER_POST_WINDOW, /* After rendering a window (any pass) */
};
enum eInputType {
INPUT_TYPE_AXIS = 0,
INPUT_TYPE_BUTTON,
INPUT_TYPE_DRAG_START,
INPUT_TYPE_DRAG_END,
INPUT_TYPE_MOTION
};
struct SCallbackInfo {
bool cancelled = false; /* on cancellable events, will cancel the event. */
};
struct SWindowDecorationExtents {
Vector2D topLeft;
Vector2D bottomRight;
//
SWindowDecorationExtents operator*(const double& scale) const {
return SWindowDecorationExtents{topLeft * scale, bottomRight * scale};
}
SWindowDecorationExtents round() {
return {topLeft.round(), bottomRight.round()};
}
bool operator==(const SWindowDecorationExtents& other) const {
return topLeft == other.topLeft && bottomRight == other.bottomRight;
}
}; };

View file

@ -2,6 +2,7 @@
#include "Compositor.hpp" #include "Compositor.hpp"
#include "render/decorations/CHyprDropShadowDecoration.hpp" #include "render/decorations/CHyprDropShadowDecoration.hpp"
#include "render/decorations/CHyprGroupBarDecoration.hpp" #include "render/decorations/CHyprGroupBarDecoration.hpp"
#include "render/decorations/CHyprBorderDecoration.hpp"
CWindow::CWindow() { CWindow::CWindow() {
m_vRealPosition.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE); m_vRealPosition.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE);
@ -13,7 +14,8 @@ CWindow::CWindow() {
m_cRealShadowColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), (void*)this, AVARDAMAGE_SHADOW); m_cRealShadowColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), (void*)this, AVARDAMAGE_SHADOW);
m_fDimPercent.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), (void*)this, AVARDAMAGE_ENTIRE); m_fDimPercent.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), (void*)this, AVARDAMAGE_ENTIRE);
m_dWindowDecorations.emplace_back(std::make_unique<CHyprDropShadowDecoration>(this)); // put the shadow so it's the first deco (has to be rendered first) addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(this));
addWindowDeco(std::make_unique<CHyprBorderDecoration>(this));
} }
CWindow::~CWindow() { CWindow::~CWindow() {
@ -21,6 +23,12 @@ CWindow::~CWindow() {
g_pCompositor->m_pLastFocus = nullptr; g_pCompositor->m_pLastFocus = nullptr;
g_pCompositor->m_pLastWindow = nullptr; g_pCompositor->m_pLastWindow = nullptr;
} }
if (!g_pHyprOpenGL)
return;
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first == this; });
} }
SWindowDecorationExtents CWindow::getFullWindowExtents() { SWindowDecorationExtents CWindow::getFullWindowExtents() {
@ -37,30 +45,27 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}}; SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
for (auto& wd : m_dWindowDecorations) { const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this);
const auto EXTENTS = wd->getWindowDecorationExtents(); if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x;
if (EXTENTS.topLeft.x > maxExtents.topLeft.x) if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.x = EXTENTS.topLeft.x; maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y) if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.topLeft.y = EXTENTS.topLeft.y; maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x) if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.x = EXTENTS.bottomRight.x; maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
}
if (m_pWLSurface.exists() && !m_bIsX11) { if (m_pWLSurface.exists() && !m_bIsX11) {
wlr_box surfaceExtents = {0, 0, 0, 0}; CBox surfaceExtents = {0, 0, 0, 0};
// TODO: this could be better, perhaps make a getFullWindowRegion? // TODO: this could be better, perhaps make a getFullWindowRegion?
wlr_xdg_surface_for_each_popup_surface( wlr_xdg_surface_for_each_popup_surface(
m_uSurface.xdg, m_uSurface.xdg,
[](wlr_surface* surf, int sx, int sy, void* data) { [](wlr_surface* surf, int sx, int sy, void* data) {
wlr_box* pSurfaceExtents = (wlr_box*)data; CBox* pSurfaceExtents = (CBox*)data;
if (sx < pSurfaceExtents->x) if (sx < pSurfaceExtents->x)
pSurfaceExtents->x = sx; pSurfaceExtents->x = sx;
if (sy < pSurfaceExtents->y) if (sy < pSurfaceExtents->y)
@ -88,21 +93,21 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
return maxExtents; return maxExtents;
} }
wlr_box CWindow::getFullWindowBoundingBox() { CBox CWindow::getFullWindowBoundingBox() {
if (m_sAdditionalConfigData.dimAround) { if (m_sAdditionalConfigData.dimAround) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
} }
auto maxExtents = getFullWindowExtents(); auto maxExtents = getFullWindowExtents();
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y, 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}; m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
return finalBox; return finalBox;
} }
wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() { CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
@ -113,7 +118,7 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
POS = PMONITOR->vecPosition; POS = PMONITOR->vecPosition;
SIZE = PMONITOR->vecSize; SIZE = PMONITOR->vecSize;
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
} }
if (DELTALESSTHAN(POS.y - PMONITOR->vecPosition.y, PMONITOR->vecReservedTopLeft.y, 1)) { if (DELTALESSTHAN(POS.y - PMONITOR->vecPosition.y, PMONITOR->vecReservedTopLeft.y, 1)) {
@ -131,10 +136,10 @@ wlr_box CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
SIZE.y += PMONITOR->vecReservedBottomRight.y; SIZE.y += PMONITOR->vecReservedBottomRight.y;
} }
return wlr_box{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y};
} }
wlr_box CWindow::getWindowInputBox() { CBox CWindow::getWindowInputBox() {
const int BORDERSIZE = getRealBorderSize(); const int BORDERSIZE = getRealBorderSize();
if (m_sAdditionalConfigData.dimAround) { if (m_sAdditionalConfigData.dimAround) {
@ -144,58 +149,45 @@ wlr_box CWindow::getWindowInputBox() {
SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}}; SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
for (auto& wd : m_dWindowDecorations) { const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this, true);
if (!wd->allowsInput()) if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
continue; maxExtents.topLeft.x = EXTENTS.topLeft.x;
const auto EXTENTS = wd->getWindowDecorationExtents(); if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y;
if (EXTENTS.topLeft.x > maxExtents.topLeft.x) if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
maxExtents.topLeft.x = EXTENTS.topLeft.x; maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
if (EXTENTS.topLeft.y > maxExtents.topLeft.y) if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
maxExtents.topLeft.y = EXTENTS.topLeft.y; maxExtents.bottomRight.y = EXTENTS.bottomRight.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 // Add extents to the real base BB and return
wlr_box finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y, 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}; m_vRealSize.vec().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize.vec().y + maxExtents.topLeft.y + maxExtents.bottomRight.y};
return finalBox; return finalBox;
} }
CBox CWindow::getWindowMainSurfaceBox() {
return {m_vRealPosition.vec().x, m_vRealPosition.vec().y, m_vRealSize.vec().x, m_vRealSize.vec().y};
}
SWindowDecorationExtents CWindow::getFullWindowReservedArea() { SWindowDecorationExtents CWindow::getFullWindowReservedArea() {
SWindowDecorationExtents extents; return g_pDecorationPositioner->getWindowDecorationReserved(this);
for (auto& wd : m_dWindowDecorations) {
const auto RESERVED = wd->getWindowDecorationReservedArea();
if (RESERVED.bottomRight == Vector2D{} && RESERVED.topLeft == Vector2D{})
continue;
extents.topLeft = extents.topLeft + RESERVED.topLeft;
extents.bottomRight = extents.bottomRight + RESERVED.bottomRight;
}
return extents;
} }
void CWindow::updateWindowDecos() { void CWindow::updateWindowDecos() {
for (auto& wd : m_dWindowDecorations)
wd->updateWindow(this);
bool recalc = false; bool recalc = false;
if (!m_bIsMapped || isHidden())
return;
for (auto& wd : m_vDecosToRemove) { for (auto& wd : m_vDecosToRemove) {
for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) { for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) {
if (it->get() == wd) { if (it->get() == wd) {
g_pDecorationPositioner->uncacheDecoration(it->get());
it = m_dWindowDecorations.erase(it); it = m_dWindowDecorations.erase(it);
recalc = true; recalc = true;
if (it == m_dWindowDecorations.end()) if (it == m_dWindowDecorations.end())
@ -204,10 +196,54 @@ void CWindow::updateWindowDecos() {
} }
} }
g_pDecorationPositioner->onWindowUpdate(this);
if (recalc) if (recalc)
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this); g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
m_vDecosToRemove.clear(); m_vDecosToRemove.clear();
for (auto& wd : m_dWindowDecorations) {
wd->updateWindow(this);
}
}
void CWindow::addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco) {
m_dWindowDecorations.emplace_back(std::move(deco));
g_pDecorationPositioner->forceRecalcFor(this);
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
}
void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
m_vDecosToRemove.push_back(deco);
g_pDecorationPositioner->forceRecalcFor(this);
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
}
void CWindow::uncacheWindowDecos() {
for (auto& wd : m_dWindowDecorations) {
g_pDecorationPositioner->uncacheDecoration(wd.get());
}
}
bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoords, std::any data) {
if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords))
return false;
for (auto& wd : m_dWindowDecorations) {
if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
continue;
if (!g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords))
continue;
if (wd->onInputOnDeco(type, mouseCoords, data))
return true;
}
return false;
} }
pid_t CWindow::getPID() { pid_t CWindow::getPID() {
@ -219,6 +255,9 @@ pid_t CWindow::getPID() {
wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr); wl_client_get_credentials(wl_resource_get_client(m_uSurface.xdg->resource), &PID, nullptr, nullptr);
} else { } else {
if (!m_bIsMapped)
return -1;
PID = m_uSurface.xwayland->pid; PID = m_uSurface.xwayland->pid;
} }
@ -279,7 +318,7 @@ void CWindow::destroyToplevelHandle() {
} }
void CWindow::updateToplevel() { void CWindow::updateToplevel() {
updateSurfaceOutputs(); updateSurfaceScaleTransformDetails();
if (!m_phForeignToplevel) if (!m_phForeignToplevel)
return; return;
@ -306,8 +345,8 @@ void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) {
wlr_surface_send_leave(pSurface, OUTPUT); wlr_surface_send_leave(pSurface, OUTPUT);
} }
void CWindow::updateSurfaceOutputs() { void CWindow::updateSurfaceScaleTransformDetails() {
if (m_iLastSurfaceMonitorID == m_iMonitorID || !m_bIsMapped || m_bHidden || !m_bMappedX11) if (!m_bIsMapped || m_bHidden)
return; return;
const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID); const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID);
@ -316,16 +355,24 @@ void CWindow::updateSurfaceOutputs() {
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) if (PNEWMONITOR != PLASTMONITOR) {
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); 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( wlr_surface_for_each_surface(
m_pWLSurface.wlr(), m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) { [](wlr_surface* surf, int x, int y, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID);
g_pProtocolManager->m_pFractionalScaleProtocolManager->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); this);
} }
@ -334,6 +381,10 @@ void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID == workspaceID) if (m_iWorkspaceID == workspaceID)
return; return;
static auto* const PCLOSEONLASTSPECIAL = &g_pConfigManager->getConfigValuePtr("misc:close_special_on_empty")->intValue;
const int OLDWORKSPACE = m_iWorkspaceID;
m_iWorkspaceID = workspaceID; m_iWorkspaceID = workspaceID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
@ -352,6 +403,15 @@ void CWindow::moveToWorkspace(int workspaceID) {
// update xwayland coords // update xwayland coords
g_pXWaylandManager->setWindowSize(this, m_vRealSize.vec()); g_pXWaylandManager->setWindowSize(this, m_vRealSize.vec());
if (g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE) && g_pCompositor->getWindowsOnWorkspace(OLDWORKSPACE) == 0 && *PCLOSEONLASTSPECIAL) {
const auto PWS = g_pCompositor->getWorkspaceByID(OLDWORKSPACE);
if (PWS) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWS->m_iMonitorID); PMONITOR)
PMONITOR->setSpecialWorkspace(nullptr);
}
}
} }
CWindow* CWindow::X11TransientFor() { CWindow* CWindow::X11TransientFor() {
@ -414,11 +474,19 @@ void CWindow::onUnmap() {
if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID) if (PMONITOR && PMONITOR->specialWorkspaceID == m_iWorkspaceID)
PMONITOR->setSpecialWorkspace(nullptr); PMONITOR->setSpecialWorkspace(nullptr);
} }
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient == this)
PMONITOR->solitaryClient = nullptr;
g_pCompositor->updateWorkspaceWindows(m_iWorkspaceID);
} }
void CWindow::onMap() { void CWindow::onMap() {
m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(this)); m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(this));
m_pWLSurface.m_pOwner = this;
// JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped) // JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped)
m_vRealPosition.resetAllCallbacks(); m_vRealPosition.resetAllCallbacks();
@ -448,6 +516,8 @@ void CWindow::onMap() {
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this, hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->surface->events.unmap : &m_uSurface.xdg->surface->events.unmap, &Events::listener_unmapWindow, this,
"CWindow"); "CWindow");
m_vReportedSize = m_vPendingReportedSize;
} }
void CWindow::onBorderAngleAnimEnd(void* ptr) { void CWindow::onBorderAngleAnimEnd(void* ptr) {
@ -472,6 +542,8 @@ void CWindow::setHidden(bool hidden) {
if (hidden && g_pCompositor->m_pLastWindow == this) { if (hidden && g_pCompositor->m_pLastWindow == this) {
g_pCompositor->m_pLastWindow = nullptr; g_pCompositor->m_pLastWindow = nullptr;
} }
setSuspended(hidden);
} }
bool CWindow::isHidden() { bool CWindow::isHidden() {
@ -492,15 +564,19 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} else if (r.szRule == "opaque") { } else if (r.szRule == "opaque") {
if (!m_sAdditionalConfigData.forceOpaqueOverridden) if (!m_sAdditionalConfigData.forceOpaqueOverridden)
m_sAdditionalConfigData.forceOpaque = true; m_sAdditionalConfigData.forceOpaque = true;
} else if (r.szRule.find("rounding") == 0) { } else if (r.szRule == "immediate") {
m_sAdditionalConfigData.forceTearing = true;
} else if (r.szRule == "nearestneighbor") {
m_sAdditionalConfigData.nearestNeighbor = true;
} else if (r.szRule.starts_with("rounding")) {
try { try {
m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Rounding rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "Rounding rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("bordersize") == 0) { } else if (r.szRule.starts_with("bordersize")) {
try { try {
m_sAdditionalConfigData.borderSize = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); m_sAdditionalConfigData.borderSize = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1));
} catch (std::exception& e) { Debug::log(ERR, "Bordersize rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "Bordersize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.find("opacity") == 0) { } else if (r.szRule.starts_with("opacity")) {
try { try {
CVarList vars(r.szRule, 0, ' '); CVarList vars(r.szRule, 0, ' ');
@ -533,37 +609,78 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "noanim") { } else if (r.szRule == "noanim") {
m_sAdditionalConfigData.forceNoAnims = true; m_sAdditionalConfigData.forceNoAnims = true;
} else if (r.szRule.find("animation") == 0) { } else if (r.szRule.starts_with("animation")) {
auto STYLE = r.szRule.substr(r.szRule.find_first_of(' ') + 1); auto STYLE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
m_sAdditionalConfigData.animationStyle = STYLE; m_sAdditionalConfigData.animationStyle = STYLE;
} else if (r.szRule.find("bordercolor") == 0) { } else if (r.szRule.starts_with("bordercolor")) {
try { try {
std::string colorPart = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); // Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true);
if (colorPart.contains(' ')) { // Basic form has only two colors, everything else can be parsed as a gradient
// we have a space, 2 values if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' '))); m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0])));
m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1)); m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1])));
} else { return;
m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart); }
for (auto& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
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"))
inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_vColors.push_back(configStringToInt(token));
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)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule);
else if (activeBorderGradient.m_vColors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule);
else if (inactiveBorderGradient.m_vColors.empty())
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
else {
m_sSpecialRenderData.activeBorderColor = activeBorderGradient;
m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient;
} }
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "dimaround") { } else if (r.szRule == "dimaround") {
m_sAdditionalConfigData.dimAround = true; m_sAdditionalConfigData.dimAround = true;
} else if (r.szRule == "keepaspectratio") { } else if (r.szRule == "keepaspectratio") {
m_sAdditionalConfigData.keepAspectRatio = true; m_sAdditionalConfigData.keepAspectRatio = true;
} else if (r.szRule.find("xray") == 0) { } else if (r.szRule.starts_with("xray")) {
CVarList vars(r.szRule, 0, ' '); CVarList vars(r.szRule, 0, ' ');
try { try {
m_sAdditionalConfigData.xray = configStringToInt(vars[1]); m_sAdditionalConfigData.xray = configStringToInt(vars[1]);
} catch (...) {} } catch (...) {}
} else if (r.szRule.starts_with("idleinhibit")) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
if (IDLERULE == "none")
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
else if (IDLERULE == "always")
m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
else if (IDLERULE == "focus")
m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
else if (IDLERULE == "fullscreen")
m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
} }
} }
void CWindow::updateDynamicRules() { void CWindow::updateDynamicRules() {
m_sSpecialRenderData.activeBorderColor = -1; m_sSpecialRenderData.activeBorderColor = CGradientValueData();
m_sSpecialRenderData.inactiveBorderColor = -1; m_sSpecialRenderData.inactiveBorderColor = CGradientValueData();
m_sSpecialRenderData.alpha = 1.f; m_sSpecialRenderData.alpha = 1.f;
m_sSpecialRenderData.alphaInactive = -1.f; m_sSpecialRenderData.alphaInactive = -1.f;
m_sAdditionalConfigData.forceNoBlur = false; m_sAdditionalConfigData.forceNoBlur = false;
@ -580,6 +697,9 @@ void CWindow::updateDynamicRules() {
m_sAdditionalConfigData.borderSize = -1; m_sAdditionalConfigData.borderSize = -1;
m_sAdditionalConfigData.keepAspectRatio = false; m_sAdditionalConfigData.keepAspectRatio = false;
m_sAdditionalConfigData.xray = -1; m_sAdditionalConfigData.xray = -1;
m_sAdditionalConfigData.forceTearing = false;
m_sAdditionalConfigData.nearestNeighbor = false;
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this); const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this);
for (auto& r : WINDOWRULES) { for (auto& r : WINDOWRULES) {
@ -622,9 +742,9 @@ bool CWindow::isInCurvedCorner(double x, double y) {
void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) { void findExtensionForVector2D(wlr_surface* surface, int x, int y, void* data) {
const auto DATA = (SExtensionFindingData*)data; const auto DATA = (SExtensionFindingData*)data;
wlr_box box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height}; CBox box = {DATA->origin.x + x, DATA->origin.y + y, surface->current.width, surface->current.height};
if (wlr_box_contains_point(&box, DATA->vec.x, DATA->vec.y)) if (box.containsPoint(DATA->vec))
*DATA->found = surface; *DATA->found = surface;
} }
@ -654,14 +774,14 @@ void CWindow::createGroup() {
Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle); Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle);
return; return;
} }
if (!m_sGroupData.pNextWindow) { if (!m_sGroupData.pNextWindow) {
m_sGroupData.pNextWindow = this; m_sGroupData.pNextWindow = this;
m_sGroupData.head = true; m_sGroupData.head = true;
m_sGroupData.locked = false; m_sGroupData.locked = false;
m_sGroupData.deny = false; m_sGroupData.deny = false;
m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(this)); addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(this));
updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(this); g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
g_pCompositor->updateAllWindowsAnimatedDecorationValues(); g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@ -735,6 +855,15 @@ int CWindow::getGroupSize() {
return size; return size;
} }
bool CWindow::canBeGroupedInto(CWindow* pWindow) {
return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged
&& ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or
|| (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked
&& !(m_sGroupData.pNextWindow && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !m_sGroupData.deny // source is not denied entry
&& !(m_eGroupRules & GROUP_BARRED && m_bFirstMap); // group rule doesn't prevent adding window
}
CWindow* CWindow::getGroupWindowByIndex(int index) { CWindow* CWindow::getGroupWindowByIndex(int index) {
const int SIZE = getGroupSize(); const int SIZE = getGroupSize();
index = ((index % SIZE) + SIZE) % SIZE; index = ((index % SIZE) + SIZE) % SIZE;
@ -791,6 +920,8 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode); g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
g_pHyprRenderer->damageWindow(pWindow); g_pHyprRenderer->damageWindow(pWindow);
pWindow->updateWindowDecos();
} }
void CWindow::insertWindowToGroup(CWindow* pWindow) { void CWindow::insertWindowToGroup(CWindow* pWindow) {
@ -798,7 +929,7 @@ void CWindow::insertWindowToGroup(CWindow* pWindow) {
const auto ENDAT = m_sGroupData.pNextWindow; const auto ENDAT = m_sGroupData.pNextWindow;
if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow)); pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
if (!pWindow->m_sGroupData.pNextWindow) { if (!pWindow->m_sGroupData.pNextWindow) {
BEGINAT->m_sGroupData.pNextWindow = pWindow; BEGINAT->m_sGroupData.pNextWindow = pWindow;
@ -874,6 +1005,9 @@ bool CWindow::opaque() {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
if (m_pWLSurface.small() && !m_pWLSurface.m_bFillIgnoreSmall)
return false;
if (PWORKSPACE->m_fAlpha.fl() != 1.f) if (PWORKSPACE->m_fAlpha.fl() != 1.f)
return false; return false;
@ -895,7 +1029,7 @@ float CWindow::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 rounding; return m_sSpecialRenderData.rounding ? rounding : 0;
} }
void CWindow::updateSpecialRenderData() { void CWindow::updateSpecialRenderData() {
@ -925,3 +1059,23 @@ int CWindow::getRealBorderSize() {
return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; return g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
} }
bool CWindow::canBeTorn() {
return (m_sAdditionalConfigData.forceTearing.toUnderlying() || m_bTearingHint) && g_pHyprRenderer->m_bTearingEnvSatisfied;
}
bool CWindow::shouldSendFullscreenState() {
const auto MODE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID)->m_efFullscreenMode;
return m_bFakeFullscreenState || (m_bIsFullscreen && (MODE == FULLSCREEN_FULL));
}
void CWindow::setSuspended(bool suspend) {
if (suspend == m_bSuspended)
return;
if (m_bIsX11)
return;
wlr_xdg_toplevel_set_suspended(m_uSurface.xdg->toplevel, suspend);
m_bSuspended = suspend;
}

View file

@ -30,6 +30,8 @@ enum eGroupRules {
GROUP_OVERRIDE = 1 << 6, // Override other rules GROUP_OVERRIDE = 1 << 6, // Override other rules
}; };
class IWindowTransformer;
template <typename T> template <typename T>
class CWindowOverridableVar { class CWindowOverridableVar {
public: public:
@ -104,13 +106,13 @@ class CWindowOverridableVar {
}; };
struct SWindowSpecialRenderData { struct SWindowSpecialRenderData {
CWindowOverridableVar<bool> alphaOverride = false; CWindowOverridableVar<bool> alphaOverride = false;
CWindowOverridableVar<float> alpha = 1.f; CWindowOverridableVar<float> alpha = 1.f;
CWindowOverridableVar<bool> alphaInactiveOverride = false; CWindowOverridableVar<bool> alphaInactiveOverride = false;
CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset CWindowOverridableVar<float> alphaInactive = -1.f; // -1 means unset
CWindowOverridableVar<int64_t> activeBorderColor = -1; // -1 means unset CWindowOverridableVar<CGradientValueData> activeBorderColor = CGradientValueData(); // empty color vector means unset
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset CWindowOverridableVar<CGradientValueData> inactiveBorderColor = CGradientValueData(); // empty color vector means unset
// set by the layout // set by the layout
CWindowOverridableVar<int> borderSize = -1; // -1 means unset CWindowOverridableVar<int> borderSize = -1; // -1 means unset
@ -138,6 +140,8 @@ struct SWindowAdditionalConfigData {
CWindowOverridableVar<bool> keepAspectRatio = false; CWindowOverridableVar<bool> keepAspectRatio = false;
CWindowOverridableVar<int> xray = -1; // -1 means unset, takes precedence over the renderdata one CWindowOverridableVar<int> xray = -1; // -1 means unset, takes precedence over the renderdata one
CWindowOverridableVar<int> borderSize = -1; // -1 means unset, takes precedence over the renderdata one CWindowOverridableVar<int> borderSize = -1; // -1 means unset, takes precedence over the renderdata one
CWindowOverridableVar<bool> forceTearing = false;
CWindowOverridableVar<bool> nearestNeighbor = false;
}; };
struct SWindowRule { struct SWindowRule {
@ -147,11 +151,15 @@ struct SWindowRule {
bool v2 = false; bool v2 = false;
std::string szTitle; std::string szTitle;
std::string szClass; std::string szClass;
int bX11 = -1; // -1 means "ANY" std::string szInitialTitle;
int bFloating = -1; std::string szInitialClass;
int bFullscreen = -1; int bX11 = -1; // -1 means "ANY"
int bPinned = -1; int bFloating = -1;
std::string szWorkspace = ""; // empty means any int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
int iOnWorkspace = -1;
std::string szWorkspace = ""; // empty means any
}; };
class CWindow { class CWindow {
@ -179,6 +187,7 @@ class CWindow {
DYNLISTENER(setOverrideRedirect); DYNLISTENER(setOverrideRedirect);
DYNLISTENER(associateX11); DYNLISTENER(associateX11);
DYNLISTENER(dissociateX11); DYNLISTENER(dissociateX11);
DYNLISTENER(ackConfigure);
// DYNLISTENER(newSubsurfaceWindow); // DYNLISTENER(newSubsurfaceWindow);
CWLSurface m_pWLSurface; CWLSurface m_pWLSurface;
@ -198,8 +207,11 @@ class CWindow {
CAnimatedVariable m_vRealSize; CAnimatedVariable m_vRealSize;
// for not spamming the protocols // for not spamming the protocols
Vector2D m_vReportedPosition; Vector2D m_vReportedPosition;
Vector2D m_vReportedSize; Vector2D m_vReportedSize;
Vector2D m_vPendingReportedSize;
std::optional<std::pair<uint32_t, Vector2D>> m_pPendingSizeAck;
std::vector<std::pair<uint32_t, Vector2D>> m_vPendingSizeAcks;
// for restoring floating statuses // for restoring floating statuses
Vector2D m_vLastFloatingSize; Vector2D m_vLastFloatingSize;
@ -229,7 +241,6 @@ class CWindow {
// XWayland stuff // XWayland stuff
bool m_bIsX11 = false; bool m_bIsX11 = false;
bool m_bMappedX11 = false;
CWindow* m_pX11Parent = nullptr; CWindow* m_pX11Parent = nullptr;
uint64_t m_iX11Type = 0; uint64_t m_iX11Type = 0;
bool m_bIsModal = false; bool m_bIsModal = false;
@ -286,6 +297,9 @@ class CWindow {
SWindowSpecialRenderData m_sSpecialRenderData; SWindowSpecialRenderData m_sSpecialRenderData;
SWindowAdditionalConfigData m_sAdditionalConfigData; SWindowAdditionalConfigData m_sAdditionalConfigData;
// Transformers
std::vector<std::unique_ptr<IWindowTransformer>> m_vTransformers;
// for alpha // for alpha
CAnimatedVariable m_fActiveInactiveAlpha; CAnimatedVariable m_fActiveInactiveAlpha;
@ -317,6 +331,8 @@ class CWindow {
} m_sGroupData; } m_sGroupData;
uint16_t m_eGroupRules = GROUP_NONE; uint16_t m_eGroupRules = GROUP_NONE;
bool m_bTearingHint = false;
// For the list lookup // For the list lookup
bool operator==(const CWindow& rhs) { bool operator==(const CWindow& rhs) {
return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && return m_uSurface.xdg == rhs.m_uSurface.xdg && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize &&
@ -324,18 +340,23 @@ class CWindow {
} }
// methods // methods
wlr_box getFullWindowBoundingBox(); CBox getFullWindowBoundingBox();
SWindowDecorationExtents getFullWindowExtents(); SWindowDecorationExtents getFullWindowExtents();
wlr_box getWindowInputBox(); CBox getWindowInputBox();
wlr_box getWindowIdealBoundingBoxIgnoreReserved(); CBox getWindowMainSurfaceBox();
CBox getWindowIdealBoundingBoxIgnoreReserved();
void addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
void updateWindowDecos(); void updateWindowDecos();
void removeWindowDeco(IHyprWindowDecoration* deco);
void uncacheWindowDecos();
bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {});
pid_t getPID(); pid_t getPID();
IHyprWindowDecoration* getDecorationByType(eDecorationType); IHyprWindowDecoration* getDecorationByType(eDecorationType);
void removeDecorationByType(eDecorationType); void removeDecorationByType(eDecorationType);
void createToplevelHandle(); void createToplevelHandle();
void destroyToplevelHandle(); void destroyToplevelHandle();
void updateToplevel(); void updateToplevel();
void updateSurfaceOutputs(); void updateSurfaceScaleTransformDetails();
void moveToWorkspace(int); void moveToWorkspace(int);
CWindow* X11TransientFor(); CWindow* X11TransientFor();
void onUnmap(); void onUnmap();
@ -348,6 +369,9 @@ class CWindow {
Vector2D middle(); Vector2D middle();
bool opaque(); bool opaque();
float rounding(); float rounding();
bool canBeTorn();
bool shouldSendFullscreenState();
void setSuspended(bool suspend);
int getRealBorderSize(); int getRealBorderSize();
void updateSpecialRenderData(); void updateSpecialRenderData();
@ -365,6 +389,7 @@ class CWindow {
CWindow* getGroupPrevious(); CWindow* getGroupPrevious();
CWindow* getGroupWindowByIndex(int); CWindow* getGroupWindowByIndex(int);
int getGroupSize(); int getGroupSize();
bool canBeGroupedInto(CWindow* pWindow);
void setGroupCurrent(CWindow* pWindow); void setGroupCurrent(CWindow* pWindow);
void insertWindowToGroup(CWindow* pWindow); void insertWindowToGroup(CWindow* pWindow);
void updateGroupOutputs(); void updateGroupOutputs();
@ -372,7 +397,8 @@ class CWindow {
private: private:
// For hidden windows and stuff // For hidden windows and stuff
bool m_bHidden = false; bool m_bHidden = false;
bool m_bSuspended = false;
}; };
/** /**
@ -414,4 +440,4 @@ struct std::formatter<CWindow*, CharT> : std::formatter<CharT> {
std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w)); std::format_to(out, ", class: {}", g_pXWaylandManager->getAppIDClass(w));
return std::format_to(out, "]"); return std::format_to(out, "]");
} }
}; };

View file

@ -16,6 +16,7 @@ class ICustomConfigValueData {
class CGradientValueData : public ICustomConfigValueData { class CGradientValueData : public ICustomConfigValueData {
public: public:
CGradientValueData(){};
CGradientValueData(CColor col) { CGradientValueData(CColor col) {
m_vColors.push_back(col); m_vColors.push_back(col);
}; };

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@
#include <algorithm> #include <algorithm>
#include <regex> #include <regex>
#include <optional> #include <optional>
#include <functional>
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include "../Window.hpp" #include "../Window.hpp"
#include "../helpers/WLClasses.hpp" #include "../helpers/WLClasses.hpp"
@ -36,18 +37,21 @@ struct SConfigValue {
}; };
struct SWorkspaceRule { struct SWorkspaceRule {
std::string monitor = ""; std::string monitor = "";
std::string workspaceString = ""; std::string workspaceString = "";
std::string workspaceName = ""; std::string workspaceName = "";
int workspaceId = -1; int workspaceId = -1;
bool isDefault = false; bool isDefault = false;
std::optional<int64_t> gapsIn; bool isPersistent = false;
std::optional<int64_t> gapsOut; std::optional<int64_t> gapsIn;
std::optional<int64_t> borderSize; std::optional<int64_t> gapsOut;
std::optional<int> border; std::optional<int64_t> borderSize;
std::optional<int> rounding; std::optional<int> border;
std::optional<int> decorate; std::optional<int> rounding;
std::optional<int> shadow; std::optional<int> decorate;
std::optional<int> shadow;
std::optional<std::string> onCreatedEmptyRunCmd;
std::map<std::string, std::string> layoutopts;
}; };
struct SMonitorAdditionalReservedArea { struct SMonitorAdditionalReservedArea {
@ -69,6 +73,12 @@ struct SAnimationPropertyConfig {
SAnimationPropertyConfig* pParentAnimation = nullptr; SAnimationPropertyConfig* pParentAnimation = nullptr;
}; };
struct SPluginKeyword {
HANDLE handle = 0;
std::string name = "";
std::function<void(const std::string&, const std::string&)> fn;
};
struct SExecRequestedRule { struct SExecRequestedRule {
std::string szRule = ""; std::string szRule = "";
uint64_t iPid = 0; uint64_t iPid = 0;
@ -83,13 +93,16 @@ class CConfigManager {
int getInt(const std::string&); int getInt(const std::string&);
float getFloat(const std::string&); float getFloat(const std::string&);
Vector2D getVec(const std::string&);
std::string getString(const std::string&); std::string getString(const std::string&);
void setFloat(const std::string&, float); void setFloat(const std::string&, float);
void setInt(const std::string&, int); void setInt(const std::string&, int);
void setVec(const std::string&, Vector2D);
void setString(const std::string&, const std::string&); void setString(const std::string&, const std::string&);
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(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 = ""); std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = "");
bool deviceConfigExists(const std::string&); bool deviceConfigExists(const std::string&);
bool shouldBlurLS(const std::string&); bool shouldBlurLS(const std::string&);
@ -115,7 +128,8 @@ class CConfigManager {
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig(); std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
void addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value); void addPluginConfigVar(HANDLE handle, const std::string& name, const SConfigValue& value);
void removePluginConfig(HANDLE handle); void addPluginKeyword(HANDLE handle, const std::string& name, std::function<void(const std::string& cmd, const std::string& val)> fun);
void removePluginConfig(HANDLE handle);
// no-op when done. // no-op when done.
void dispatchExecOnce(); void dispatchExecOnce();
@ -158,6 +172,7 @@ class CConfigManager {
std::vector<std::string> m_vDeclaredPlugins; std::vector<std::string> m_vDeclaredPlugins;
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
std::vector<SPluginKeyword> pluginKeywords;
bool isFirstLaunch = true; // For exec-once bool isFirstLaunch = true; // For exec-once

View file

@ -28,8 +28,14 @@ monitor=,preferred,auto,auto
# Source a file (multi-file configs) # Source a file (multi-file configs)
# source = ~/.config/hypr/myColors.conf # source = ~/.config/hypr/myColors.conf
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
# Some default env vars. # Some default env vars.
env = XCURSOR_SIZE,24 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/ # For all categories, see https://wiki.hyprland.org/Configuring/Variables/
input { input {
@ -58,6 +64,9 @@ general {
col.inactive_border = rgba(595959aa) col.inactive_border = rgba(595959aa)
layout = dwindle layout = dwindle
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
} }
decoration { decoration {
@ -108,6 +117,11 @@ gestures {
workspace_swipe = off workspace_swipe = off
} }
misc {
# See https://wiki.hyprland.org/Configuring/Variables/ for more
force_default_wallpaper = -1 # Set to 0 to disable the anime mascot wallpapers
}
# Example per-device config # Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more # See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
device:epic-mouse-v1 { device:epic-mouse-v1 {
@ -119,18 +133,19 @@ device:epic-mouse-v1 {
# Example windowrule v2 # Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more # See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
windowrulev2 = nomaximizerequest, class:.* # You'll probably like this.
# See https://wiki.hyprland.org/Configuring/Keywords/ for more # See https://wiki.hyprland.org/Configuring/Keywords/ for more
$mainMod = SUPER $mainMod = SUPER
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more # Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, kitty bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive, bind = $mainMod, C, killactive,
bind = $mainMod, M, exit, bind = $mainMod, M, exit,
bind = $mainMod, E, exec, dolphin bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating, bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, wofi --show drun bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, togglesplit, # dwindle bind = $mainMod, J, togglesplit, # dwindle
@ -164,6 +179,10 @@ bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9 bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10 bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, magic
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
# Scroll through existing workspaces with mainMod + scroll # Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1 bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1 bind = $mainMod, mouse_up, workspace, e-1

View file

@ -1,7 +1,6 @@
#include "CrashReporter.hpp" #include "CrashReporter.hpp"
#include <random> #include <random>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <execinfo.h>
#include <fstream> #include <fstream>
#include <signal.h> #include <signal.h>
@ -64,8 +63,8 @@ void CrashReporter::createAndSaveCrash(int sig) {
struct utsname unameInfo; struct utsname unameInfo;
uname(&unameInfo); uname(&unameInfo);
finalCrashReport += finalCrashReport += std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", std::string{unameInfo.sysname}, std::string{unameInfo.nodename},
std::format("\tSystem name: {}\n\tNode name: {}\n\tRelease: {}\n\tVersion: {}\n\n", unameInfo.sysname, unameInfo.nodename, unameInfo.release, unameInfo.version); std::string{unameInfo.release}, std::string{unameInfo.version});
#if defined(__DragonFly__) || defined(__FreeBSD__) #if defined(__DragonFly__) || defined(__FreeBSD__)
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga"); const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
@ -120,7 +119,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += "\n\nLog tail:\n"; finalCrashReport += "\n\nLog tail:\n";
finalCrashReport += execAndGet(("cat \"" + Debug::logFile + "\" | tail -n 50").c_str()); finalCrashReport += Debug::rollingLog;
const auto HOME = getenv("HOME"); const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME"); const auto CACHE_HOME = getenv("XDG_CACHE_HOME");
@ -130,25 +129,19 @@ void CrashReporter::createAndSaveCrash(int sig) {
std::ofstream ofs; std::ofstream ofs;
std::string path; std::string path;
if (!CACHE_HOME) { if (!CACHE_HOME || std::string(CACHE_HOME).empty()) {
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) { if (!std::filesystem::exists(std::string(HOME) + "/.hyprland"))
std::filesystem::create_directory(std::string(HOME) + "/.hyprland"); std::filesystem::create_directory(std::string(HOME) + "/.hyprland");
std::filesystem::permissions(std::string(HOME) + "/.hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
path = std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt"; path = std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc); ofs.open(path, std::ios::trunc);
} else if (CACHE_HOME) { } else {
if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland")) { if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland"))
std::filesystem::create_directory(std::string(CACHE_HOME) + "/hyprland"); std::filesystem::create_directory(std::string(CACHE_HOME) + "/hyprland");
std::filesystem::permissions(std::string(CACHE_HOME) + "/hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
path = std::string(CACHE_HOME) + "/hyprland/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);
} else {
return;
} }
ofs << finalCrashReport; ofs << finalCrashReport;

View file

@ -7,6 +7,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h>
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@ -28,13 +29,22 @@ static std::string getWorkspaceNameFromSpecialID(const int workspaceID) {
return workspace->m_szName; return workspace->m_szName;
} }
std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string monitorsRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
CVarList vars(request, 0, ' ');
auto allMonitors = false;
if (vars.size() > 2)
return "too many args";
if (vars.size() == 2 && vars[1] == "all")
allMonitors = true;
std::string result = ""; std::string result = "";
if (format == HyprCtl::FORMAT_JSON) { if (format == HyprCtl::FORMAT_JSON) {
result += "["; result += "[";
for (auto& m : g_pCompositor->m_vMonitors) { for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output) if (!m->output || m->ID == -1ull)
continue; continue;
result += std::format( result += std::format(
@ -63,35 +73,37 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
"transform": {}, "transform": {},
"focused": {}, "focused": {},
"dpmsStatus": {}, "dpmsStatus": {},
"vrr": {} "vrr": {},
"activelyTearing": {}
}},)#", }},)#",
m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""), m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szDescription), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""),
(m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (m->output->serial ? m->output->serial : ""), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
(int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspace, escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName), m->activeWorkspace, (m->activeWorkspace == -1 ? "" : escapeJSONStrings(g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName)), m->specialWorkspaceID,
m->specialWorkspaceID, escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, escapeJSONStrings(getWorkspaceNameFromSpecialID(m->specialWorkspaceID)), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "true" : "false"), (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false")); (m->dpmsStatus ? "true" : "false"), (m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"),
m->tearingState.activelyTearing ? "true" : "false");
} }
trimTrailingComma(result); trimTrailingComma(result);
result += "]"; result += "]";
} else { } else {
for (auto& m : g_pCompositor->m_vMonitors) { for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output) if (!m->output || m->ID == -1ull)
continue; continue;
result += result +=
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial " std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial "
"workspace: {} ({})\n\treserved: {} " "workspace: {} ({})\n\treserved: {} "
"{} {} {}\n\tscale: {:.2f}\n\ttransform: " "{} {} {}\n\tscale: {:.2f}\n\ttransform: "
"{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\n", "{}\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->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szDescription,
(m->output->description ? m->output->description : ""), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspace,
(m->output->serial ? m->output->serial : ""), m->activeWorkspace, g_pCompositor->getWorkspaceByID(m->activeWorkspace)->m_szName, m->specialWorkspaceID, (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, getWorkspaceNameFromSpecialID(m->specialWorkspaceID), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x,
(int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m.get() == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus,
(int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)); (int)(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED), m->tearingState.activelyTearing);
} }
} }
@ -103,33 +115,34 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
if (!w->m_sGroupData.pNextWindow) if (!w->m_sGroupData.pNextWindow)
return isJson ? "" : "0"; return isJson ? "" : "0";
std::vector<CWindow*> groupMembers;
CWindow* curr = w;
do {
groupMembers.push_back(curr);
curr = curr->m_sGroupData.pNextWindow;
} while (curr != w);
const auto comma = isJson ? ", " : ",";
std::ostringstream result; std::ostringstream result;
bool first = true; CWindow* head = w->getGroupHead();
for (auto& gw : groupMembers) { CWindow* curr = head;
if (first) while (true) {
first = false;
else
result << comma;
if (isJson) if (isJson)
result << std::format("\"0x{:x}\"", (uintptr_t)gw); result << std::format("\"0x{:x}\"", (uintptr_t)curr);
else else
result << std::format("{:x}", (uintptr_t)gw); result << std::format("{:x}", (uintptr_t)curr);
curr = curr->m_sGroupData.pNextWindow;
// We've wrapped around to the start, break out without trailing comma
if (curr == head)
break;
result << (isJson ? ", " : ",");
} }
return result.str(); return result.str();
} }
static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat format) { static std::string getWindowData(CWindow* w, HyprCtl::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)
return i;
}
return -1;
};
if (format == HyprCtl::FORMAT_JSON) { if (format == HyprCtl::FORMAT_JSON) {
return std::format( return std::format(
R"#({{ R"#({{
@ -155,7 +168,8 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
"fullscreenMode": {}, "fullscreenMode": {},
"fakeFullscreen": {}, "fakeFullscreen": {},
"grouped": [{}], "grouped": [{}],
"swallowing": "0x{:x}" "swallowing": "0x{:x}",
"focusHistoryID": {}
}},)#", }},)#",
(uintptr_t)w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (uintptr_t)w, (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID, (int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
@ -166,13 +180,13 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(), escapeJSONStrings(g_pXWaylandManager->getTitle(w)), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(),
((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"),
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? (int)g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0), (w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? (int)g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed); w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
} else { } else {
return std::format( return std::format(
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
"{}\n\txwayland: {}\n\tpinned: " "{}\n\txwayland: {}\n\tpinned: "
"{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\n", "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n",
(uintptr_t)w, w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (uintptr_t)w, w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y,
(int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID, (int)w->m_vRealSize.goalv().x, (int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
(w->m_iWorkspaceID == -1 ? "" : (w->m_iWorkspaceID == -1 ? "" :
@ -181,7 +195,7 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
(int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle, (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w), g_pXWaylandManager->getTitle(w), w->m_szInitialClass, w->m_szInitialTitle,
w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0), (w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
(int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed); (int)w->m_bFakeFullscreenState, getGroupedData(w, format), (uintptr_t)w->m_pSwallowed, getFocusHistoryID(w));
} }
} }
@ -213,23 +227,70 @@ static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat
"id": {}, "id": {},
"name": "{}", "name": "{}",
"monitor": "{}", "monitor": "{}",
"monitorID": {},
"windows": {}, "windows": {},
"hasfullscreen": {}, "hasfullscreen": {},
"lastwindow": "0x{:x}", "lastwindow": "0x{:x}",
"lastwindowtitle": "{}" "lastwindowtitle": "{}"
}})#", }})#",
w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), g_pCompositor->getWindowsOnWorkspace(w->m_iID), w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"),
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : ""); ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), (uintptr_t)PLASTW, PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "");
} else { } else {
return std::format("workspace ID {} ({}) on monitor {}:\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID, w->m_szName, return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\n", w->m_iID,
PMONITOR ? PMONITOR->szName : "?", g_pCompositor->getWindowsOnWorkspace(w->m_iID), (int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW, w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", g_pCompositor->getWindowsOnWorkspace(w->m_iID),
PLASTW ? PLASTW->m_szTitle : ""); (int)w->m_bHasFullscreenWindow, (uintptr_t)PLASTW, PLASTW ? PLASTW->m_szTitle : "");
} }
} }
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, HyprCtl::eHyprCtlOutputFormat format) {
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == HyprCtl::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 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())) : "";
const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : "";
const std::string shadow = (bool)(r.shadow) ? std::format(",\n \"shadow\": {}", boolToString(r.shadow.value())) : "";
std::string result = std::format(R"#({{
"workspaceString": "{}"{}{}{}{}{}{}{}{}
}})#",
escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate, shadow);
return result;
} else {
const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "<unset>" : escapeJSONStrings(r.monitor));
const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : "<unset>");
const std::string gapsIn = std::format("\tgapsIn: {}\n", (bool)(r.gapsIn) ? std::to_string(r.gapsIn.value()) : "<unset>");
const std::string gapsOut = std::format("\tgapsOut: {}\n", (bool)(r.gapsOut) ? std::to_string(r.gapsOut.value()) : "<unset>");
const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : "<unset>");
const std::string border = std::format("\tborder: {}\n", (bool)(r.border) ? boolToString(r.border.value()) : "<unset>");
const std::string rounding = std::format("\trounding: {}\n", (bool)(r.rounding) ? boolToString(r.rounding.value()) : "<unset>");
const std::string decorate = std::format("\tdecorate: {}\n", (bool)(r.decorate) ? boolToString(r.decorate.value()) : "<unset>");
const std::string shadow = std::format("\tshadow: {}\n", (bool)(r.shadow) ? boolToString(r.shadow.value()) : "<unset>");
std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut,
borderSize, border, rounding, decorate, shadow);
return result;
}
}
std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
if (!g_pCompositor->m_pLastMonitor)
return "unsafe state";
std::string result = ""; std::string result = "";
auto w = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace); auto w = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
if (!w)
return "internal error";
return getWorkspaceData(w, format); return getWorkspaceData(w, format);
} }
@ -254,6 +315,26 @@ std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string workspaceRulesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
result += ",";
}
trimTrailingComma(result);
result += "]";
} else {
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
}
}
return result;
}
std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string activeWindowRequest(HyprCtl::eHyprCtlOutputFormat format) {
const auto PWINDOW = g_pCompositor->m_pLastWindow; const auto PWINDOW = g_pCompositor->m_pLastWindow;
@ -342,6 +423,28 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result; return result;
} }
std::string layoutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format(
R"#(
"{}",)#",
m);
}
trimTrailingComma(result);
result += "\n]\n";
} else {
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format("{}\n", m);
}
}
return result;
}
std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = ""; std::string result = "";
@ -554,6 +657,20 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret; return ret;
} }
std::string rollinglogRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
result += "[\n\"log\":\"";
result += escapeJSONStrings(Debug::rollingLog);
result += "\"]";
} else {
result = Debug::rollingLog;
}
return result;
}
std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) { std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string ret = ""; std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts(); const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
@ -632,15 +749,12 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg + std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nTag: " + GIT_TAG + "\n\nflags: (if any)\n"; ").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
#ifdef LEGACY_RENDERER #ifdef LEGACY_RENDERER
result += "legacyrenderer\n"; result += "legacyrenderer\n";
#endif #endif
#ifndef NDEBUG #ifndef ISDEBUG
result += "debug\n";
#endif
#ifdef HYPRLAND_DEBUG
result += "debug\n"; result += "debug\n";
#endif #endif
#ifdef NO_XWAYLAND #ifdef NO_XWAYLAND
@ -655,17 +769,15 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
"commit": "{}", "commit": "{}",
"dirty": {}, "dirty": {},
"commit_message": "{}", "commit_message": "{}",
"commit_date": "{}",
"tag": "{}", "tag": "{}",
"flags": [)#", "flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_TAG); GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG);
#ifdef LEGACY_RENDERER #ifdef LEGACY_RENDERER
result += "\"legacyrenderer\","; result += "\"legacyrenderer\",";
#endif #endif
#ifndef NDEBUG #ifndef ISDEBUG
result += "\"debug\",";
#endif
#ifdef HYPRLAND_DEBUG
result += "\"debug\","; result += "\"debug\",";
#endif #endif
#ifdef NO_XWAYLAND #ifdef NO_XWAYLAND
@ -682,6 +794,39 @@ std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ""; // make the compiler happy return ""; // make the compiler happy
} }
std::string systemInfoRequest() {
std::string result = versionRequest(HyprCtl::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(std::string in) { std::string dispatchRequest(std::string in) {
// get rid of the dispatch keyword // get rid of the dispatch keyword
in = in.substr(in.find_first_of(' ') + 1); in = in.substr(in.find_first_of(' ') + 1);
@ -952,7 +1097,7 @@ std::string dispatchSetProp(std::string request) {
bool lock = false; bool lock = false;
if (vars.size() > 4) { if (vars.size() > 4) {
if (vars[4].find("lock") == 0) { if (vars[4].starts_with("lock")) {
lock = true; lock = true;
} }
} }
@ -993,15 +1138,19 @@ std::string dispatchSetProp(std::string request) {
} else if (PROP == "alphainactive") { } else if (PROP == "alphainactive") {
PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock); PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock);
} else if (PROP == "activebordercolor") { } else if (PROP == "activebordercolor") {
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "inactivebordercolor") { } else if (PROP == "inactivebordercolor") {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock);
} else if (PROP == "forcergbx") { } else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "bordersize") { } else if (PROP == "bordersize") {
PWINDOW->m_sSpecialRenderData.borderSize.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sSpecialRenderData.borderSize.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "keepaspectratio") { } else if (PROP == "keepaspectratio") {
PWINDOW->m_sAdditionalConfigData.keepAspectRatio.forceSetIgnoreLocked(configStringToInt(VAL), lock); PWINDOW->m_sAdditionalConfigData.keepAspectRatio.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "immediate") {
PWINDOW->m_sAdditionalConfigData.forceTearing.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "nearestneighbor") {
PWINDOW->m_sAdditionalConfigData.nearestNeighbor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else { } else {
return "prop not found"; return "prop not found";
} }
@ -1059,6 +1208,32 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
} }
} }
std::string decorationRequest(std::string request, HyprCtl::eHyprCtlOutputFormat format) {
CVarList vars(request, 0, ' ');
const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[1]);
if (!PWINDOW)
return "none";
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
result += "[";
for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
}
trimTrailingComma(result);
result += "]";
} else {
result = +"Decoration\tPriority\n";
for (auto& wd : PWINDOW->m_dWindowDecorations) {
result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n";
}
}
return result;
}
void createOutputIter(wlr_backend* backend, void* data) { void createOutputIter(wlr_backend* backend, void* data) {
const auto DATA = (std::pair<std::string, bool>*)data; const auto DATA = (std::pair<std::string, bool>*)data;
@ -1155,7 +1330,7 @@ std::string dispatchPlugin(std::string request) {
const auto PLUGIN = g_pPluginSystem->loadPlugin(PATH); const auto PLUGIN = g_pPluginSystem->loadPlugin(PATH);
if (!PLUGIN) if (!PLUGIN)
return "error in loading plugin"; return "error in loading plugin, last error: " + g_pPluginSystem->m_szLastError;
} else if (OPERATION == "unload") { } else if (OPERATION == "unload") {
if (vars.size() < 3) if (vars.size() < 3)
return "not enough args"; return "not enough args";
@ -1227,13 +1402,20 @@ std::string getReply(std::string request) {
auto format = HyprCtl::FORMAT_NORMAL; auto format = HyprCtl::FORMAT_NORMAL;
// process flags for non-batch requests // process flags for non-batch requests
if (!request.contains("[[BATCH]]") && request.contains("/")) { if (!request.starts_with("[[BATCH]]") && request.contains("/")) {
long unsigned int sepIndex = 0; long unsigned int sepIndex = 0;
for (const auto& c : request) { for (const auto& c : request) {
if (c == '/') { // stop at separator if (c == '/') { // stop at separator
break; 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++; sepIndex++;
if (c == 'j') if (c == 'j')
@ -1244,10 +1426,12 @@ std::string getReply(std::string request) {
request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string request = request.substr(sepIndex + 1); // remove flags and separator so we can compare the rest of the string
} }
if (request == "monitors") if (request.starts_with("monitors"))
return monitorsRequest(format); return monitorsRequest(request, format);
else if (request == "workspaces") else if (request == "workspaces")
return workspacesRequest(format); return workspacesRequest(format);
else if (request == "workspacerules")
return workspaceRulesRequest(format);
else if (request == "activeworkspace") else if (request == "activeworkspace")
return activeWorkspaceRequest(format); return activeWorkspaceRequest(format);
else if (request == "clients") else if (request == "clients")
@ -1260,7 +1444,7 @@ std::string getReply(std::string request) {
return layersRequest(format); return layersRequest(format);
else if (request == "version") else if (request == "version")
return versionRequest(format); return versionRequest(format);
else if (request.find("reload") == 0) else if (request.starts_with("reload"))
return reloadRequest(request); return reloadRequest(request);
else if (request == "devices") else if (request == "devices")
return devicesRequest(format); return devicesRequest(format);
@ -1272,29 +1456,37 @@ std::string getReply(std::string request) {
return bindsRequest(format); return bindsRequest(format);
else if (request == "globalshortcuts") else if (request == "globalshortcuts")
return globalShortcutsRequest(format); return globalShortcutsRequest(format);
else if (request == "systeminfo")
return systemInfoRequest();
else if (request == "animations") else if (request == "animations")
return animationsRequest(format); return animationsRequest(format);
else if (request.find("plugin") == 0) else if (request == "rollinglog")
return rollinglogRequest(format);
else if (request == "layouts")
return layoutsRequest(format);
else if (request.starts_with("plugin"))
return dispatchPlugin(request); return dispatchPlugin(request);
else if (request.find("notify") == 0) else if (request.starts_with("notify"))
return dispatchNotify(request); return dispatchNotify(request);
else if (request.find("setprop") == 0) else if (request.starts_with("setprop"))
return dispatchSetProp(request); return dispatchSetProp(request);
else if (request.find("seterror") == 0) else if (request.starts_with("seterror"))
return dispatchSeterror(request); return dispatchSeterror(request);
else if (request.find("switchxkblayout") == 0) else if (request.starts_with("switchxkblayout"))
return switchXKBLayoutRequest(request); return switchXKBLayoutRequest(request);
else if (request.find("output") == 0) else if (request.starts_with("output"))
return dispatchOutput(request); return dispatchOutput(request);
else if (request.find("dispatch") == 0) else if (request.starts_with("dispatch"))
return dispatchRequest(request); return dispatchRequest(request);
else if (request.find("keyword") == 0) else if (request.starts_with("keyword"))
return dispatchKeyword(request); return dispatchKeyword(request);
else if (request.find("setcursor") == 0) else if (request.starts_with("setcursor"))
return dispatchSetCursor(request); return dispatchSetCursor(request);
else if (request.find("getoption") == 0) else if (request.starts_with("getoption"))
return dispatchGetOption(request, format); return dispatchGetOption(request, format);
else if (request.find("[[BATCH]]") == 0) else if (request.starts_with("decorations"))
return decorationRequest(request, format);
else if (request.starts_with("[[BATCH]]"))
return dispatchBatch(request); return dispatchBatch(request);
return "unknown request"; return "unknown request";
@ -1308,14 +1500,14 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP)
return 0; return 0;
sockaddr_in clientAddress; sockaddr_in clientAddress;
socklen_t clientSize = sizeof(clientAddress); socklen_t clientSize = sizeof(clientAddress);
const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC); const auto ACCEPTEDCONNECTION = accept4(HyprCtl::iSocketFD, (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC);
char readBuffer[1024]; std::array<char, 1024> readBuffer;
fd_set fdset; fd_set fdset;
FD_ZERO(&fdset); FD_ZERO(&fdset);
FD_SET(ACCEPTEDCONNECTION, &fdset); FD_SET(ACCEPTEDCONNECTION, &fdset);
timeval timeout = {.tv_sec = 0, .tv_usec = 5000}; timeval timeout = {.tv_sec = 0, .tv_usec = 5000};
@ -1326,10 +1518,17 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
return 0; return 0;
} }
auto messageSize = read(ACCEPTEDCONNECTION, readBuffer, 1024); std::string request;
readBuffer[messageSize == 1024 ? 1023 : messageSize] = '\0'; while (true) {
readBuffer.fill(0);
std::string request(readBuffer); auto messageSize = read(ACCEPTEDCONNECTION, readBuffer.data(), 1023);
if (messageSize < 1)
break;
std::string recvd = readBuffer.data();
request += recvd;
if (messageSize < 1023)
break;
}
std::string reply = ""; std::string reply = "";

View file

@ -233,6 +233,6 @@ void CHyprDebugOverlay::draw() {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
wlr_box pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; CBox pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f); g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
} }

View file

@ -24,7 +24,7 @@ class CHyprMonitorDebugOverlay {
std::deque<float> m_dLastAnimationTicks; std::deque<float> m_dLastAnimationTicks;
std::chrono::high_resolution_clock::time_point m_tpLastFrame; std::chrono::high_resolution_clock::time_point m_tpLastFrame;
CMonitor* m_pMonitor = nullptr; CMonitor* m_pMonitor = nullptr;
wlr_box m_wbLastDrawnBox; CBox m_wbLastDrawnBox;
friend class CHyprRenderer; friend class CHyprRenderer;
}; };

View file

@ -3,7 +3,7 @@
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
CHyprNotificationOverlay::CHyprNotificationOverlay() { CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) { g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (m_dNotifications.size() == 0) if (m_dNotifications.size() == 0)
return; return;
@ -44,9 +44,13 @@ void CHyprNotificationOverlay::addNotification(const std::string& text, const CC
PNOTIF->started.reset(); PNOTIF->started.reset();
PNOTIF->timeMs = timeMs; PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon; PNOTIF->icon = icon;
for (auto& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get());
}
} }
wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) { CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
static constexpr auto ANIM_DURATION_MS = 600.0; static constexpr auto ANIM_DURATION_MS = 600.0;
static constexpr auto ANIM_LAG_MS = 100.0; static constexpr auto ANIM_LAG_MS = 100.0;
static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0; static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0;
@ -166,7 +170,7 @@ wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
// cleanup notifs // cleanup notifs
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; }); std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
return wlr_box{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10}; return CBox{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
} }
void CHyprNotificationOverlay::draw(CMonitor* pMonitor) { void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
@ -197,7 +201,7 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
cairo_surface_flush(m_pCairoSurface); cairo_surface_flush(m_pCairoSurface);
wlr_box damage = drawNotifications(pMonitor); CBox damage = drawNotifications(pMonitor);
g_pHyprRenderer->damageBox(&damage); g_pHyprRenderer->damageBox(&damage);
g_pHyprRenderer->damageBox(&m_bLastDamage); g_pHyprRenderer->damageBox(&m_bLastDamage);
@ -220,6 +224,6 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
wlr_box pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y}; CBox pMonBox = {0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y};
g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f); g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f);
} }

View file

@ -10,16 +10,15 @@
#include <cairo/cairo.h> #include <cairo/cairo.h>
enum eIconBackend enum eIconBackend {
{
ICONS_BACKEND_NONE = 0, ICONS_BACKEND_NONE = 0,
ICONS_BACKEND_NF, ICONS_BACKEND_NF,
ICONS_BACKEND_FA ICONS_BACKEND_FA
}; };
static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = { static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = {
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "󰸞", ""}, std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""},
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}}; std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "󰸞", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
static const std::array<CColor, ICON_NONE + 1> ICONS_COLORS = {CColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0}, static const std::array<CColor, ICON_NONE + 1> ICONS_COLORS = {CColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0},
CColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0}, CColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
CColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0}, CColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
@ -44,8 +43,8 @@ class CHyprNotificationOverlay {
void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE); void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE);
private: private:
wlr_box drawNotifications(CMonitor* pMonitor); CBox drawNotifications(CMonitor* pMonitor);
wlr_box m_bLastDamage; CBox m_bLastDamage;
std::deque<std::unique_ptr<SNotification>> m_dNotifications; std::deque<std::unique_ptr<SNotification>> m_dNotifications;

View file

@ -10,19 +10,24 @@ void Debug::init(const std::string& IS) {
} }
void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) { void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
char* outputStr = nullptr; if (level > wlr_log_get_verbosity())
return;
std::ofstream ofs; char* outputStr = nullptr;
ofs.open(logFile, std::ios::out | std::ios::app);
vasprintf(&outputStr, fmt, args); vasprintf(&outputStr, fmt, args);
std::string output = std::string(outputStr); std::string output = std::string(outputStr);
free(outputStr); free(outputStr);
ofs << "[wlr] " << output << "\n"; rollingLog += output + "\n";
ofs.close(); if (!disableLogs || !*disableLogs) {
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << "[wlr] " << output << "\n";
ofs.close();
}
if (!disableStdout) if (!disableStdout)
std::cout << output << "\n"; std::cout << output << "\n";

View file

@ -1,13 +1,14 @@
#pragma once #pragma once
#include <string> #include <string>
#include <wlr/util/log.h>
#include <format> #include <format>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <chrono> #include <chrono>
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp" #include "../helpers/MiscFunctions.hpp"
#define LOGMESSAGESIZE 1024 #define LOGMESSAGESIZE 1024
#define ROLLING_LOG_SIZE 4096
enum LogLevel { enum LogLevel {
NONE = -1, NONE = -1,
@ -25,14 +26,17 @@ namespace Debug {
inline int64_t* disableTime = nullptr; inline int64_t* disableTime = nullptr;
inline bool disableStdout = false; inline bool disableStdout = false;
inline bool trace = false; inline bool trace = false;
inline bool shuttingDown = false;
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 <typename... Args> template <typename... Args>
void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) { void log(LogLevel level, std::format_string<Args...> fmt, Args&&... args) {
if (disableLogs && *disableLogs) if (level == TRACE && !trace)
return; return;
if (level == TRACE && !trace) if (shuttingDown)
return; return;
std::string logMsg = ""; std::string logMsg = "";
@ -47,10 +51,6 @@ namespace Debug {
default: break; default: break;
} }
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
// print date and time to the ofs // print date and time to the ofs
if (disableTime && !*disableTime) { if (disableTime && !*disableTime) {
#ifndef _LIBCPP_VERSION #ifndef _LIBCPP_VERSION
@ -69,9 +69,18 @@ namespace Debug {
// 3. this is actually what std::format in stdlib does // 3. this is actually what std::format in stdlib does
logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
ofs << logMsg << "\n"; rollingLog += logMsg + "\n";
if (rollingLog.size() > ROLLING_LOG_SIZE)
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
ofs.close(); if (!disableLogs || !*disableLogs) {
// log to a file
std::ofstream ofs;
ofs.open(logFile, std::ios::out | std::ios::app);
ofs << logMsg << "\n";
ofs.close();
}
// log it to the stdout too. // log it to the stdout too.
if (!disableStdout) if (!disableStdout)
@ -79,4 +88,4 @@ namespace Debug {
} }
void wlrLog(wlr_log_importance level, const char* fmt, va_list args); void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
}; };

View file

@ -13,15 +13,6 @@ inline PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp" #include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp"
inline void loadGLProc(void* pProc, const char* name) {
void* proc = (void*)eglGetProcAddress(name);
if (proc == NULL) {
Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name);
abort();
}
*(void**)pProc = proc;
}
#define TRACY_GPU_CONTEXT TracyGpuContext #define TRACY_GPU_CONTEXT TracyGpuContext
#define TRACY_GPU_ZONE(e) TracyGpuZone(e) #define TRACY_GPU_ZONE(e) TracyGpuZone(e)
#define TRACY_GPU_COLLECT TracyGpuCollect #define TRACY_GPU_COLLECT TracyGpuCollect

View file

@ -42,7 +42,7 @@ namespace Events {
DYNLISTENFUNC(repositionPopupXDG); DYNLISTENFUNC(repositionPopupXDG);
// Surface XDG (window) // Surface XDG (window)
LISTENER(newXDGSurface); LISTENER(newXDGToplevel);
LISTENER(activateXDG); LISTENER(activateXDG);
// Window events // Window events
@ -62,6 +62,7 @@ namespace Events {
DYNLISTENFUNC(setOverrideRedirect); DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(associateX11); DYNLISTENFUNC(associateX11);
DYNLISTENFUNC(dissociateX11); DYNLISTENFUNC(dissociateX11);
DYNLISTENFUNC(ackConfigure);
// Window subsurfaces // Window subsurfaces
// LISTENER(newSubsurfaceWindow); // LISTENER(newSubsurfaceWindow);
@ -120,10 +121,6 @@ namespace Events {
DYNLISTENFUNC(destroyDragIcon); DYNLISTENFUNC(destroyDragIcon);
DYNLISTENFUNC(commitDragIcon); DYNLISTENFUNC(commitDragIcon);
// Inhibit
LISTENER(InhibitActivate);
LISTENER(InhibitDeactivate);
// Deco XDG // Deco XDG
LISTENER(NewXDGDeco); LISTENER(NewXDGDeco);
@ -174,4 +171,7 @@ namespace Events {
// Cursor shape // Cursor shape
LISTENER(setCursorShape); LISTENER(setCursorShape);
// Tearing hints
LISTENER(newTearingHint);
}; };

View file

@ -95,8 +95,8 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) {
PMONITOR->scheduledRecalc = true; PMONITOR->scheduledRecalc = true;
// and damage // and damage
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width, CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height}; layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
} }
@ -142,7 +142,7 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output); wlr_surface_send_enter(layersurface->layerSurface->surface, layersurface->layerSurface->output);
const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive && const bool GRABSFOCUS = layersurface->layerSurface->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
// don't focus if constrained // don't focus if constrained
(!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint); (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint);
@ -157,8 +157,8 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
layersurface->position = Vector2D(layersurface->geometry.x, layersurface->geometry.y); layersurface->position = Vector2D(layersurface->geometry.x, layersurface->geometry.y);
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width, CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height}; layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace); const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL; const bool FULLSCREEN = WORKSPACE->m_bHasFullscreenWindow && WORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL;
@ -171,7 +171,8 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")}); g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("openLayer", layersurface); EMIT_HOOK_EVENT("openLayer", layersurface);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale); g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
} }
void Events::listener_unmapLayerSurface(void* owner, void* data) { void Events::listener_unmapLayerSurface(void* owner, void* data) {
@ -246,13 +247,15 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
} }
} }
wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width, CBox geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width,
layersurface->geometry.height}; layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
geomFixed = {layersurface->geometry.x + (int)PMONITOR->vecPosition.x, layersurface->geometry.y + (int)PMONITOR->vecPosition.y, geomFixed = {layersurface->geometry.x + (int)PMONITOR->vecPosition.x, layersurface->geometry.y + (int)PMONITOR->vecPosition.y,
(int)layersurface->layerSurface->surface->current.width, (int)layersurface->layerSurface->surface->current.height}; (int)layersurface->layerSurface->surface->current.width, (int)layersurface->layerSurface->surface->current.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
g_pInputManager->sendMotionEventsToFocused();
} }
void Events::listener_commitLayerSurface(void* owner, void* data) { void Events::listener_commitLayerSurface(void* owner, void* data) {
@ -269,7 +272,7 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
if (layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) if (layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layersurface->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
wlr_box geomFixed = {layersurface->geometry.x, layersurface->geometry.y, layersurface->geometry.width, layersurface->geometry.height}; CBox geomFixed = {layersurface->geometry.x, layersurface->geometry.y, layersurface->geometry.width, layersurface->geometry.height};
g_pHyprRenderer->damageBox(&geomFixed); g_pHyprRenderer->damageBox(&geomFixed);
// fix if it changed its mon // fix if it changed its mon
@ -342,5 +345,6 @@ void Events::listener_commitLayerSurface(void* owner, void* data) {
g_pHyprRenderer->damageSurface(layersurface->layerSurface->surface, layersurface->position.x, layersurface->position.y); g_pHyprRenderer->damageSurface(layersurface->layerSurface->surface, layersurface->position.x, layersurface->position.y);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale); g_pCompositor->setPreferredScaleForSurface(layersurface->layerSurface->surface, PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(layersurface->layerSurface->surface, PMONITOR->transform);
} }

View file

@ -156,20 +156,6 @@ void Events::listener_commitDragIcon(void* owner, void* data) {
Debug::log(LOG, "Drag icon committed."); Debug::log(LOG, "Drag icon committed.");
} }
void Events::listener_InhibitActivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Activated exclusive for {:x}.", (uintptr_t)g_pCompositor->m_sSeat.exclusiveClient);
g_pInputManager->refocus();
g_pCompositor->m_sSeat.exclusiveClient = g_pCompositor->m_sWLRInhibitMgr->active_client;
}
void Events::listener_InhibitDeactivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Deactivated exclusive.");
g_pCompositor->m_sSeat.exclusiveClient = nullptr;
g_pInputManager->refocus();
}
void Events::listener_RendererDestroy(wl_listener* listener, void* data) { void Events::listener_RendererDestroy(wl_listener* listener, void* data) {
Debug::log(LOG, "!!Renderer destroyed!!"); Debug::log(LOG, "!!Renderer destroyed!!");
} }
@ -237,3 +223,43 @@ void Events::listener_setCursorShape(wl_listener* listener, void* data) {
g_pInputManager->processMouseRequest(E); g_pInputManager->processMouseRequest(E);
} }
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);
const auto NEWCTRL = g_pHyprRenderer->m_vTearingControllers.emplace_back(std::make_unique<STearingController>()).get();
NEWCTRL->pWlrHint = (wlr_tearing_control_v1*)data;
NEWCTRL->hyprListener_destroy.initCallback(
&NEWCTRL->pWlrHint->events.destroy,
[&](void* owner, void* data) {
Debug::log(LOG, "Destroyed {} tearing hint", (uintptr_t)((STearingController*)owner)->pWlrHint);
std::erase_if(g_pHyprRenderer->m_vTearingControllers, [&](const auto& other) { return other.get() == owner; });
},
NEWCTRL, "TearingController");
NEWCTRL->hyprListener_set.initCallback(
&NEWCTRL->pWlrHint->events.set_hint,
[&](void* owner, void* data) {
const auto TEARINGHINT = (STearingController*)owner;
const auto PWINDOW = g_pCompositor->getWindowFromSurface(TEARINGHINT->pWlrHint->surface);
if (PWINDOW) {
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->current);
}
},
NEWCTRL, "TearingController");
}

View file

@ -22,14 +22,18 @@ void Events::listener_change(wl_listener* listener, void* data) {
if (!CONFIG) if (!CONFIG)
return; return;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output) if (!m->output)
continue; continue;
if (g_pCompositor->m_pUnsafeOutput == m.get())
continue;
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output); const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
wlr_box BOX; CBox BOX;
wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, &BOX); wlr_output_layout_get_box(g_pCompositor->m_sWLROutputLayout, m->output, BOX.pWlr());
BOX.applyFromWlr();
//m->vecSize.x = BOX.width; //m->vecSize.x = BOX.width;
// m->vecSize.y = BOX.height; // m->vecSize.y = BOX.height;
@ -67,52 +71,31 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
return; return;
} }
if (g_pCompositor->m_bUnsafeState)
Debug::log(WARN, "Recovering from an unsafe state. May you be lucky.");
// add it to real // add it to real
std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr; std::shared_ptr<CMonitor>* PNEWMONITORWRAP = nullptr;
PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>()); PNEWMONITORWRAP = &g_pCompositor->m_vRealMonitors.emplace_back(std::make_shared<CMonitor>());
if (std::string("HEADLESS-1") == OUTPUT->name)
g_pCompositor->m_pUnsafeOutput = PNEWMONITORWRAP->get();
(*PNEWMONITORWRAP)->ID = g_pCompositor->getNextAvailableMonitorID(OUTPUT->name); (*PNEWMONITORWRAP)->output = OUTPUT;
const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? OUTPUT == g_pCompositor->m_pUnsafeOutput->output : false;
(*PNEWMONITORWRAP)->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(OUTPUT->name);
const auto PNEWMONITOR = PNEWMONITORWRAP->get();
PNEWMONITOR->isUnsafeFallback = FALLBACK;
const auto PNEWMONITOR = PNEWMONITORWRAP->get(); if (!FALLBACK)
PNEWMONITOR->onConnect(false);
PNEWMONITOR->output = OUTPUT; if (!PNEWMONITOR->m_bEnabled || FALLBACK)
PNEWMONITOR->m_pThisWrap = PNEWMONITORWRAP; return;
PNEWMONITOR->onConnect(false); // ready to process if we have a real monitor
if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled) if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR; g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
// wlroots will instantly call this handler before we get a return to the wlr_output* in CCompositor::enterUnsafeState g_pCompositor->m_bReadyToProcess = true;
const bool PROBABLYFALLBACK = (g_pCompositor->m_bUnsafeState && !g_pCompositor->m_pUnsafeOutput) || OUTPUT == g_pCompositor->m_pUnsafeOutput;
// ready to process if we have a real monitor
if (PNEWMONITOR->m_bEnabled && !PROBABLYFALLBACK) {
// leave unsafe state
if (g_pCompositor->m_bUnsafeState) {
// recover workspaces
std::vector<CWorkspace*> wsp;
for (auto& ws : g_pCompositor->m_vWorkspaces) {
wsp.push_back(ws.get());
}
for (auto& ws : wsp) {
// because this can realloc the vec
g_pCompositor->moveWorkspaceToMonitor(ws, PNEWMONITOR);
}
g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR;
const auto POS = PNEWMONITOR->middle();
if (g_pCompositor->m_sSeat.mouse)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y);
}
g_pCompositor->m_bReadyToProcess = true;
}
g_pConfigManager->m_bWantsMonitorReload = true; g_pConfigManager->m_bWantsMonitorReload = true;
g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR); g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR);
@ -126,7 +109,7 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) { if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1; w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceOutputs(); w->updateSurfaceScaleTransformDetails();
} }
} }
} }
@ -138,7 +121,9 @@ void Events::listener_monitorFrame(void* owner, void* data) {
if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!"); Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState && PMONITOR->output != g_pCompositor->m_pUnsafeOutput) { if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) {
return m->output != g_pCompositor->m_pUnsafeOutput->output;
})) {
// restore from unsafe state // restore from unsafe state
g_pCompositor->leaveUnsafeState(); g_pCompositor->leaveUnsafeState();
} }
@ -149,12 +134,25 @@ void Events::listener_monitorFrame(void* owner, void* data) {
if (!PMONITOR->m_bEnabled) if (!PMONITOR->m_bEnabled)
return; return;
g_pHyprRenderer->recheckSolitaryForMonitor(PMONITOR);
PMONITOR->tearingState.busy = false;
if (PMONITOR->tearingState.activelyTearing && PMONITOR->solitaryClient /* can be invalidated by a recheck */) {
if (!PMONITOR->tearingState.frameScheduledWhileBusy)
return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
PMONITOR->tearingState.nextRenderTorn = true;
PMONITOR->tearingState.frameScheduledWhileBusy = false;
}
static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue; 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 PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue;
PMONITOR->lastPresentationTimer.reset(); PMONITOR->lastPresentationTimer.reset();
if (*PENABLERAT) { if (*PENABLERAT && !PMONITOR->tearingState.nextRenderTorn) {
if (!PMONITOR->RATScheduled) { if (!PMONITOR->RATScheduled) {
// render // render
g_pHyprRenderer->renderMonitor(PMONITOR); g_pHyprRenderer->renderMonitor(PMONITOR);
@ -200,14 +198,11 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name); Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
pMonitor->onDisconnect(); pMonitor->onDisconnect(true);
pMonitor->output = nullptr; pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false; pMonitor->m_bRenderingInitPassed = false;
if (g_pCompositor->m_pUnsafeOutput == OUTPUT)
g_pCompositor->m_pUnsafeOutput = nullptr;
Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName); Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; }); std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; });
@ -238,7 +233,7 @@ void Events::listener_monitorCommit(void* owner, void* data) {
const auto E = (wlr_output_event_commit*)data; const auto E = (wlr_output_event_commit*)data;
if (E->committed & WLR_OUTPUT_STATE_BUFFER) { if (E->state->committed & WLR_OUTPUT_STATE_BUFFER) {
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E); g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E); g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E);
} }

View file

@ -64,9 +64,9 @@ void createNewPopup(wlr_xdg_popup* popup, SXDGPopup* pHyprPopup) {
const auto PMONITOR = g_pCompositor->m_pLastMonitor; const auto PMONITOR = g_pCompositor->m_pLastMonitor;
wlr_box box = {.x = PMONITOR->vecPosition.x - pHyprPopup->lx, .y = PMONITOR->vecPosition.y - pHyprPopup->ly, .width = PMONITOR->vecSize.x, .height = PMONITOR->vecSize.y}; CBox box = {PMONITOR->vecPosition.x - pHyprPopup->lx, PMONITOR->vecPosition.y - pHyprPopup->ly, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
wlr_xdg_popup_unconstrain_from_box(popup, &box); wlr_xdg_popup_unconstrain_from_box(popup, box.pWlr());
pHyprPopup->monitor = PMONITOR; pHyprPopup->monitor = PMONITOR;
@ -159,13 +159,16 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0; int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly); addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents; CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents); wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2); g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
if (PPOPUP->monitor) if (PPOPUP->monitor) {
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale); g_pCompositor->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale);
g_pCompositor->setPreferredTransformForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->transform);
}
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode {:x}", (uintptr_t)PPOPUP->pSurfaceTree); Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode {:x}", (uintptr_t)PPOPUP->pSurfaceTree);
} }
@ -178,11 +181,18 @@ void Events::listener_repositionPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0; int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly); addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents; CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents); wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
PPOPUP->lastPos = {lx - extents.x, ly - extents.y}; PPOPUP->lastPos = {lx - extents.x, ly - extents.y};
PPOPUP->repositionRequested = true; PPOPUP->repositionRequested = true;
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
CBox box = {PMONITOR->vecPosition.x - lx + PPOPUP->popup->current.geometry.x, PMONITOR->vecPosition.y - ly + PPOPUP->popup->current.geometry.y, PMONITOR->vecSize.x,
PMONITOR->vecSize.y};
wlr_xdg_popup_unconstrain_from_box(PPOPUP->popup, box.pWlr());
} }
void Events::listener_unmapPopupXDG(void* owner, void* data) { void Events::listener_unmapPopupXDG(void* owner, void* data) {
@ -199,8 +209,9 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0; int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly); addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents; CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents); wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2); g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
@ -225,8 +236,9 @@ void Events::listener_commitPopupXDG(void* owner, void* data) {
int lx = 0, ly = 0; int lx = 0, ly = 0;
addPopupGlobalCoords(PPOPUP, &lx, &ly); addPopupGlobalCoords(PPOPUP, &lx, &ly);
wlr_box extents; CBox extents;
wlr_surface_get_extends(PPOPUP->popup->base->surface, &extents); wlr_surface_get_extends(PPOPUP->popup->base->surface, extents.pWlr());
extents.applyFromWlr();
if (PPOPUP->repositionRequested) if (PPOPUP->repositionRequested)
g_pHyprRenderer->damageBox(PPOPUP->lastPos.x, PPOPUP->lastPos.y, extents.width + 2, extents.height + 2); g_pHyprRenderer->damageBox(PPOPUP->lastPos.x, PPOPUP->lastPos.y, extents.width + 2, extents.height + 2);

View file

@ -46,12 +46,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
static auto* const PSWALLOW = &g_pConfigManager->getConfigValuePtr("misc:enable_swallow")->intValue; 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 PSWALLOWREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_regex")->strValue;
static auto* const PSWALLOWEXREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_exception_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;
auto PMONITOR = g_pCompositor->m_pLastMonitor; auto PMONITOR = g_pCompositor->m_pLastMonitor;
const auto PWORKSPACE = const auto PWORKSPACE =
PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace); PMONITOR->specialWorkspaceID ? g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID) : g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
PWINDOW->m_iMonitorID = PMONITOR->ID; PWINDOW->m_iMonitorID = PMONITOR->ID;
PWINDOW->m_bMappedX11 = true;
PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace; PWINDOW->m_iWorkspaceID = PMONITOR->specialWorkspaceID ? PMONITOR->specialWorkspaceID : PMONITOR->activeWorkspace;
PWINDOW->m_bIsMapped = true; PWINDOW->m_bIsMapped = true;
PWINDOW->m_bReadyToDelete = false; PWINDOW->m_bReadyToDelete = false;
@ -95,7 +95,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PWORKSPACE->m_bDefaultPseudo) { if (PWORKSPACE->m_bDefaultPseudo) {
PWINDOW->m_bIsPseudotiled = true; PWINDOW->m_bIsPseudotiled = true;
wlr_box desiredGeometry = {0}; CBox desiredGeometry = {0};
g_pXWaylandManager->getGeometryForWindow(PWINDOW, &desiredGeometry); g_pXWaylandManager->getGeometryForWindow(PWINDOW, &desiredGeometry);
PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height); PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
} }
@ -116,7 +116,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW); PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
for (auto& r : WINDOWRULES) { for (auto& r : WINDOWRULES) {
if (r.szRule.find("monitor") == 0) { if (r.szRule.starts_with("monitor")) {
try { try {
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' '))); const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' ')));
@ -150,7 +150,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW); Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r.szRule, r.szValue, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r.szRule, r.szValue, e.what()); }
} else if (r.szRule.find("workspace") == 0) { } else if (r.szRule.starts_with("workspace")) {
// check if it isnt unset // check if it isnt unset
const auto WORKSPACERQ = r.szRule.substr(r.szRule.find_first_of(' ') + 1); const auto WORKSPACERQ = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
@ -166,19 +166,19 @@ void Events::listener_mapWindow(void* owner, void* data) {
requestedWorkspace = ""; requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r.szValue); Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r.szValue);
} else if (r.szRule.find("float") == 0) { } else if (r.szRule.starts_with("float")) {
PWINDOW->m_bIsFloating = true; PWINDOW->m_bIsFloating = true;
} else if (r.szRule.find("tile") == 0) { } else if (r.szRule.starts_with("tile")) {
PWINDOW->m_bIsFloating = false; PWINDOW->m_bIsFloating = false;
} else if (r.szRule.find("pseudo") == 0) { } else if (r.szRule.starts_with("pseudo")) {
PWINDOW->m_bIsPseudotiled = true; PWINDOW->m_bIsPseudotiled = true;
} else if (r.szRule.find("nofocus") == 0) { } else if (r.szRule.starts_with("nofocus")) {
PWINDOW->m_bNoFocus = true; PWINDOW->m_bNoFocus = true;
} else if (r.szRule.find("noinitialfocus") == 0) { } else if (r.szRule.starts_with("noinitialfocus")) {
PWINDOW->m_bNoInitialFocus = true; PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.find("nofullscreenrequest") == 0) { } else if (r.szRule.starts_with("nofullscreenrequest")) {
PWINDOW->m_bNoFullscreenRequest = true; PWINDOW->m_bNoFullscreenRequest = true;
} else if (r.szRule.find("nomaximizerequest") == 0) { } else if (r.szRule.starts_with("nomaximizerequest")) {
PWINDOW->m_bNoMaximizeRequest = true; PWINDOW->m_bNoMaximizeRequest = true;
} else if (r.szRule == "fullscreen") { } else if (r.szRule == "fullscreen") {
requestsFullscreen = true; requestsFullscreen = true;
@ -198,7 +198,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
overridingNoMaximize = true; overridingNoMaximize = true;
} else if (r.szRule == "stayfocused") { } else if (r.szRule == "stayfocused") {
PWINDOW->m_bStayFocused = true; PWINDOW->m_bStayFocused = true;
} else if (r.szRule.find("group") == 0) { } else if (r.szRule.starts_with("group")) {
if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE) if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE)
continue; continue;
@ -245,31 +245,10 @@ void Events::listener_mapWindow(void* owner, void* data) {
} }
vPrev = v; vPrev = v;
} }
} else if (r.szRule.find("idleinhibit") == 0) {
auto IDLERULE = r.szRule.substr(r.szRule.find_first_of(' ') + 1);
if (IDLERULE == "none") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_NONE;
} else if (IDLERULE == "always") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS;
} else if (IDLERULE == "focus") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_FOCUS;
} else if (IDLERULE == "fullscreen") {
PWINDOW->m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN;
} else {
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
}
} }
PWINDOW->applyDynamicRule(r); PWINDOW->applyDynamicRule(r);
} }
CWindow* pFullscreenWindow = nullptr;
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
pFullscreenWindow = PFULLWINDOW;
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, PWORKSPACE->m_efFullscreenMode);
}
PWINDOW->updateSpecialRenderData(); PWINDOW->updateSpecialRenderData();
// disallow tiled pinned // disallow tiled pinned
@ -279,13 +258,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
const CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' '); const CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' ');
if (!WORKSPACEARGS[0].empty()) { if (!WORKSPACEARGS[0].empty()) {
if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].find("silent") == 0) if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].starts_with("silent"))
workspaceSilent = true; workspaceSilent = true;
std::string requestedWorkspaceName; std::string requestedWorkspaceName;
const int REQUESTEDWORKSPACEID = getWorkspaceIDFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0), requestedWorkspaceName); const int REQUESTEDWORKSPACEID = getWorkspaceIDFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0), requestedWorkspaceName);
if (REQUESTEDWORKSPACEID != INT_MAX) { if (REQUESTEDWORKSPACEID != WORKSPACE_INVALID) {
auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID); auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID);
if (!pWorkspace) if (!pWorkspace)
@ -315,7 +294,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
// size and move rules // size and move rules
for (auto& r : WINDOWRULES) { for (auto& r : WINDOWRULES) {
if (r.szRule.find("size") == 0) { if (r.szRule.starts_with("size")) {
try { try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1); const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@ -337,7 +316,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); } } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("minsize") == 0) { } else if (r.szRule.starts_with("minsize")) {
try { try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1); const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@ -351,7 +330,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule minsize failed, rule: {} -> {}", r.szRule, r.szValue); } } catch (...) { Debug::log(LOG, "Rule minsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("maxsize") == 0) { } else if (r.szRule.starts_with("maxsize")) {
try { try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1); const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
@ -365,16 +344,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule maxsize failed, rule: {} -> {}", r.szRule, r.szValue); } } catch (...) { Debug::log(LOG, "Rule maxsize failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("move") == 0) { } else if (r.szRule.starts_with("move")) {
try { try {
auto value = r.szRule.substr(r.szRule.find(' ') + 1); auto value = r.szRule.substr(r.szRule.find(' ') + 1);
const bool ONSCREEN = value.find("onscreen") == 0; const bool ONSCREEN = value.starts_with("onscreen");
if (ONSCREEN) if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1); value = value.substr(value.find_first_of(' ') + 1);
const bool CURSOR = value.find("cursor") == 0; const bool CURSOR = value.starts_with("cursor");
if (CURSOR) if (CURSOR)
value = value.substr(value.find_first_of(' ') + 1); value = value.substr(value.find_first_of(' ') + 1);
@ -385,7 +364,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
int posX = 0; int posX = 0;
int posY = 0; int posY = 0;
if (POSXSTR.find("100%-") == 0) { if (POSXSTR.starts_with("100%-")) {
const auto POSXRAW = POSXSTR.substr(5); const auto POSXRAW = POSXSTR.substr(5);
posX = posX =
PMONITOR->vecSize.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.x); PMONITOR->vecSize.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
@ -404,7 +383,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
} }
} }
if (POSYSTR.find("100%-") == 0) { if (POSYSTR.starts_with("100%-")) {
const auto POSYRAW = POSYSTR.substr(5); const auto POSYRAW = POSYSTR.substr(5);
posY = posY =
PMONITOR->vecSize.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.y); PMONITOR->vecSize.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
@ -439,7 +418,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r.szRule, r.szValue); } } catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r.szRule, r.szValue); }
} else if (r.szRule.find("center") == 0) { } else if (r.szRule.starts_with("center")) {
auto RESERVEDOFFSET = Vector2D(); auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r.szRule, 2, ' '); const auto ARGS = CVarList(r.szRule, 2, ' ');
if (ARGS[1] == "1") if (ARGS[1] == "1")
@ -474,6 +453,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus); const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus);
if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.keyboard_interactive) if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.keyboard_interactive)
PWINDOW->m_bNoInitialFocus = true; PWINDOW->m_bNoInitialFocus = true;
if (PWORKSPACE->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) {
if (*PNEWTAKESOVERFS == 0)
PWINDOW->m_bNoInitialFocus = true;
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;
else
requestsFullscreen = true;
}
if (!PWINDOW->m_bNoFocus && !PWINDOW->m_bNoInitialFocus && if (!PWINDOW->m_bNoFocus && !PWINDOW->m_bNoInitialFocus &&
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent && (PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && !workspaceSilent &&
@ -489,7 +478,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Window got assigned a surfaceTreeNode {:x}", (uintptr_t)PWINDOW->m_pSurfaceTree); Debug::log(LOG, "Window got assigned a surfaceTreeNode {:x}", (uintptr_t)PWINDOW->m_pSurfaceTree);
if (!PWINDOW->m_bIsX11) { if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xdg->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XDG Window Late"); PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.set_title, &Events::listener_setTitleWindow, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_newPopupXDG.initCallback(&PWINDOW->m_uSurface.xdg->events.new_popup, &Events::listener_newPopupXDG, PWINDOW, "XDG Window Late"); PWINDOW->hyprListener_newPopupXDG.initCallback(&PWINDOW->m_uSurface.xdg->events.new_popup, &Events::listener_newPopupXDG, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW, PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_maximize, &Events::listener_requestMaximize, PWINDOW,
@ -500,6 +488,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW, "XDG Window Late"); PWINDOW->hyprListener_requestResize.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_resize, &Events::listener_requestResize, PWINDOW, "XDG Window Late");
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW, PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xdg->toplevel->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
"XDG Window Late"); "XDG Window Late");
PWINDOW->hyprListener_ackConfigure.initCallback(&PWINDOW->m_uSurface.xdg->events.ack_configure, &Events::listener_ackConfigure, PWINDOW, "XDG Window Late");
} else { } else {
PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW, PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW,
"XWayland Window Late"); "XWayland Window Late");
@ -515,14 +504,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
"XWayland Window Late"); "XWayland Window Late");
} }
// do the animation thing
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false);
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
if ((requestsFullscreen && (!PWINDOW->m_bNoFullscreenRequest || overridingNoFullscreen)) || (requestsMaximize && (!PWINDOW->m_bNoMaximizeRequest || overridingNoMaximize)) || if ((requestsFullscreen && (!PWINDOW->m_bNoFullscreenRequest || overridingNoFullscreen)) || (requestsMaximize && (!PWINDOW->m_bNoMaximizeRequest || overridingNoMaximize)) ||
requestsFakeFullscreen) { requestsFakeFullscreen) {
// fix fullscreen on requested (basically do a switcheroo) // fix fullscreen on requested (basically do a switcheroo)
@ -543,10 +524,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
} }
} }
if (pFullscreenWindow && workspaceSilent) {
g_pCompositor->setWindowFullscreen(pFullscreenWindow, true, PWORKSPACE->m_efFullscreenMode);
}
// recheck idle inhibitors // recheck idle inhibitors
g_pInputManager->recheckIdleInhibitorStatus(); g_pInputManager->recheckIdleInhibitorStatus();
@ -640,10 +617,34 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)}); g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, g_pXWaylandManager->getAppIDClass(PWINDOW), PWINDOW->m_szTitle)});
EMIT_HOOK_EVENT("openWindow", PWINDOW); EMIT_HOOK_EVENT("openWindow", PWINDOW);
// apply data from default decos. Borders, shadows.
g_pDecorationPositioner->forceRecalcFor(PWINDOW);
PWINDOW->updateWindowDecos();
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
// do animations
g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false);
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
PWINDOW->m_fAlpha = 1.f;
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
// recalc the values for this window // recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
// avoid this window being visible
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFullscreen && !PWINDOW->m_bIsFloating)
PWINDOW->m_fAlpha.setValueAndWarp(0.f);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale); g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform);
g_pInputManager->sendMotionEventsToFocused();
// fix some xwayland apps that don't behave nicely
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize;
g_pCompositor->updateWorkspaceWindows(PWINDOW->m_iWorkspaceID);
} }
void Events::listener_unmapWindow(void* owner, void* data) { void Events::listener_unmapWindow(void* owner, void* data) {
@ -657,6 +658,13 @@ void Events::listener_unmapWindow(void* owner, void* data) {
return; return;
} }
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)}); g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW); EMIT_HOOK_EVENT("closeWindow", PWINDOW);
@ -664,7 +672,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (!PWINDOW->m_bIsX11) { if (!PWINDOW->m_bIsX11) {
Debug::log(LOG, "Unregistered late callbacks XDG"); Debug::log(LOG, "Unregistered late callbacks XDG");
PWINDOW->hyprListener_commitWindow.removeCallback();
PWINDOW->hyprListener_setTitleWindow.removeCallback(); PWINDOW->hyprListener_setTitleWindow.removeCallback();
PWINDOW->hyprListener_newPopupXDG.removeCallback(); PWINDOW->hyprListener_newPopupXDG.removeCallback();
PWINDOW->hyprListener_requestMaximize.removeCallback(); PWINDOW->hyprListener_requestMaximize.removeCallback();
@ -672,6 +679,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->hyprListener_requestMove.removeCallback(); PWINDOW->hyprListener_requestMove.removeCallback();
PWINDOW->hyprListener_requestResize.removeCallback(); PWINDOW->hyprListener_requestResize.removeCallback();
PWINDOW->hyprListener_fullscreenWindow.removeCallback(); PWINDOW->hyprListener_fullscreenWindow.removeCallback();
PWINDOW->hyprListener_ackConfigure.removeCallback();
} else { } else {
Debug::log(LOG, "Unregistered late callbacks XWL"); Debug::log(LOG, "Unregistered late callbacks XWL");
PWINDOW->hyprListener_fullscreenWindow.removeCallback(); PWINDOW->hyprListener_fullscreenWindow.removeCallback();
@ -685,13 +693,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (PWINDOW->m_bIsFullscreen) if (PWINDOW->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL); g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents();
}
// Allow the renderer to catch the last frame. // Allow the renderer to catch the last frame.
g_pHyprOpenGL->makeWindowSnapshot(PWINDOW); g_pHyprOpenGL->makeWindowSnapshot(PWINDOW);
@ -712,8 +713,6 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pInputManager->releaseAllMouseButtons(); g_pInputManager->releaseAllMouseButtons();
} }
PWINDOW->m_bMappedX11 = false;
// remove the fullscreen window status from workspace if we closed it // remove the fullscreen window status from workspace if we closed it
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
@ -731,13 +730,16 @@ void Events::listener_unmapWindow(void* owner, void* data) {
Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE); Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE);
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow) { if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow && PWINDOWCANDIDATE)
if (!PWINDOWCANDIDATE) g_pCompositor->focusWindow(PWINDOWCANDIDATE);
g_pInputManager->simulateMouseMovement();
else g_pInputManager->sendMotionEventsToFocused();
g_pCompositor->focusWindow(PWINDOWCANDIDATE);
} else { // CWindow::onUnmap will remove this window's active status, but we can't really do it above.
g_pInputManager->simulateMouseMovement(); if (PWINDOW == g_pCompositor->m_pLastWindow || !g_pCompositor->m_pLastWindow) {
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
EMIT_HOOK_EVENT("activeWindow", (CWindow*)nullptr);
} }
} else { } else {
Debug::log(LOG, "Unmapped was not focused, ignoring a refocus."); Debug::log(LOG, "Unmapped was not focused, ignoring a refocus.");
@ -774,13 +776,32 @@ void Events::listener_unmapWindow(void* owner, void* data) {
PWINDOW->onUnmap(); PWINDOW->onUnmap();
} }
void Events::listener_ackConfigure(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
const auto E = (wlr_xdg_surface_configure*)data;
// find last matching serial
const auto SERIAL = std::find_if(PWINDOW->m_vPendingSizeAcks.rbegin(), PWINDOW->m_vPendingSizeAcks.rend(), [&](const auto& e) { return e.first == E->serial; });
if (SERIAL == PWINDOW->m_vPendingSizeAcks.rend())
return;
PWINDOW->m_pPendingSizeAck = *SERIAL;
std::erase_if(PWINDOW->m_vPendingSizeAcks, [&](const auto& el) { return el.first == SERIAL->first; });
}
void Events::listener_commitWindow(void* owner, void* data) { void Events::listener_commitWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner; CWindow* PWINDOW = (CWindow*)owner;
if (!PWINDOW->m_bMappedX11 || PWINDOW->isHidden() || (PWINDOW->m_bIsX11 && !PWINDOW->m_bMappedX11)) if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden())
return; return;
PWINDOW->updateSurfaceOutputs(); if (PWINDOW->m_bIsX11)
PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
else if (PWINDOW->m_pPendingSizeAck.has_value()) {
PWINDOW->m_vReportedSize = PWINDOW->m_pPendingSizeAck->second;
PWINDOW->m_pPendingSizeAck.reset();
}
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y, 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); PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
@ -788,22 +809,29 @@ void Events::listener_commitWindow(void* owner, void* data) {
if (PWINDOW->m_bIsX11 || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen) if (PWINDOW->m_bIsX11 || !PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen)
return; return;
const auto ISRIGID = PWINDOW->m_uSurface.xdg->toplevel->current.max_height == PWINDOW->m_uSurface.xdg->toplevel->current.min_height && const auto MINSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.min_width, PWINDOW->m_uSurface.xdg->toplevel->current.min_height};
PWINDOW->m_uSurface.xdg->toplevel->current.max_width == PWINDOW->m_uSurface.xdg->toplevel->current.min_width; const auto MAXSIZE = Vector2D{PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height};
if (!ISRIGID) if (MAXSIZE < Vector2D{1, 1})
return; return;
const Vector2D REQUESTEDSIZE = {PWINDOW->m_uSurface.xdg->toplevel->current.max_width, PWINDOW->m_uSurface.xdg->toplevel->current.max_height}; const auto REALSIZE = PWINDOW->m_vRealSize.goalv();
Vector2D newSize = REALSIZE;
if (REQUESTEDSIZE == PWINDOW->m_vReportedSize || REQUESTEDSIZE.x < 5 || REQUESTEDSIZE.y < 5) if (MAXSIZE.x < newSize.x)
return; newSize.x = MAXSIZE.x;
if (MAXSIZE.y < newSize.y)
newSize.y = MAXSIZE.y;
if (MINSIZE.x > newSize.x)
newSize.x = MINSIZE.x;
if (MINSIZE.y > newSize.y)
newSize.y = MINSIZE.y;
const Vector2D DELTA = PWINDOW->m_vReportedSize - REQUESTEDSIZE; const Vector2D DELTA = REALSIZE - newSize;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0; PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goalv() + DELTA / 2.0;
PWINDOW->m_vRealSize = REQUESTEDSIZE; PWINDOW->m_vRealSize = newSize;
g_pXWaylandManager->setWindowSize(PWINDOW, REQUESTEDSIZE, true); g_pXWaylandManager->setWindowSize(PWINDOW, newSize, true);
g_pHyprRenderer->damageWindow(PWINDOW); g_pHyprRenderer->damageWindow(PWINDOW);
} }
@ -820,6 +848,7 @@ void Events::listener_destroyWindow(void* owner, void* data) {
g_pCompositor->m_pLastFocus = nullptr; g_pCompositor->m_pLastFocus = nullptr;
} }
PWINDOW->hyprListener_commitWindow.removeCallback();
PWINDOW->hyprListener_mapWindow.removeCallback(); PWINDOW->hyprListener_mapWindow.removeCallback();
PWINDOW->hyprListener_unmapWindow.removeCallback(); PWINDOW->hyprListener_unmapWindow.removeCallback();
PWINDOW->hyprListener_destroyWindow.removeCallback(); PWINDOW->hyprListener_destroyWindow.removeCallback();
@ -839,8 +868,8 @@ void Events::listener_destroyWindow(void* owner, void* data) {
PWINDOW->m_bReadyToDelete = true; PWINDOW->m_bReadyToDelete = true;
if (!PWINDOW->m_bFadingOut) { if (!PWINDOW->m_bFadingOut) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW);
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
} }
} }
@ -995,8 +1024,10 @@ void Events::listener_configureX11(void* owner, void* data) {
const auto E = (wlr_xwayland_surface_configure_event*)data; const auto E = (wlr_xwayland_surface_configure_event*)data;
if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bMappedX11) { if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bIsMapped) {
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height); wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
PWINDOW->m_vPendingReportedSize = {E->width, E->height};
PWINDOW->m_vReportedSize = {E->width, E->height};
return; return;
} }
@ -1021,8 +1052,11 @@ void Events::listener_configureX11(void* owner, void* data) {
static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue; static auto* const PXWLFORCESCALEZERO = &g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")->intValue;
if (*PXWLFORCESCALEZERO) { if (*PXWLFORCESCALEZERO) {
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) 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); 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(); PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec();
@ -1030,6 +1064,14 @@ void Events::listener_configureX11(void* owner, void* data) {
wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height); wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height);
PWINDOW->m_vPendingReportedSize = {E->width, E->height};
PWINDOW->m_vReportedSize = {E->width, E->height};
PWINDOW->updateWindowDecos();
if (!g_pCompositor->isWorkspaceVisible(PWINDOW->m_iWorkspaceID))
return; // further things are only for visible windows
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f)->activeWorkspace; PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f)->activeWorkspace;
g_pCompositor->changeWindowZOrder(PWINDOW, true); g_pCompositor->changeWindowZOrder(PWINDOW, true);
@ -1040,14 +1082,12 @@ void Events::listener_configureX11(void* owner, void* data) {
g_pInputManager->refocus(); g_pInputManager->refocus();
g_pHyprRenderer->damageWindow(PWINDOW); g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->updateWindowDecos();
} }
void Events::listener_unmanagedSetGeometry(void* owner, void* data) { void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner; CWindow* PWINDOW = (CWindow*)owner;
if (!PWINDOW->m_bMappedX11) if (!PWINDOW->m_bIsMapped)
return; return;
const auto POS = PWINDOW->m_vRealPosition.goalv(); const auto POS = PWINDOW->m_vRealPosition.goalv();
@ -1080,8 +1120,11 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->width, PWINDOW->m_uSurface.xwayland->height)); 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) 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); 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.goalv(); PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.goalv();
@ -1092,6 +1135,10 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
g_pCompositor->changeWindowZOrder(PWINDOW, true); g_pCompositor->changeWindowZOrder(PWINDOW, true);
PWINDOW->updateWindowDecos(); PWINDOW->updateWindowDecos();
g_pHyprRenderer->damageWindow(PWINDOW); g_pHyprRenderer->damageWindow(PWINDOW);
PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition.goalv();
PWINDOW->m_vReportedSize = PWINDOW->m_vRealSize.goalv();
PWINDOW->m_vPendingReportedSize = PWINDOW->m_vReportedSize;
} }
} }
@ -1107,12 +1154,14 @@ void Events::listener_associateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner; const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW, "XWayland Window"); PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW, "XWayland Window");
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XWayland Window");
} }
void Events::listener_dissociateX11(void* owner, void* data) { void Events::listener_dissociateX11(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner; const auto PWINDOW = (CWindow*)owner;
PWINDOW->hyprListener_mapWindow.removeCallback(); PWINDOW->hyprListener_mapWindow.removeCallback();
PWINDOW->hyprListener_commitWindow.removeCallback();
} }
void Events::listener_surfaceXWayland(wl_listener* listener, void* data) { void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
@ -1137,20 +1186,19 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window"); PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
} }
void Events::listener_newXDGSurface(wl_listener* listener, void* data) { void Events::listener_newXDGToplevel(wl_listener* listener, void* data) {
// A window got opened // A window got opened
const auto XDGSURFACE = (wlr_xdg_surface*)data; const auto XDGTOPLEVEL = (wlr_xdg_toplevel*)data;
const auto XDGSURFACE = XDGTOPLEVEL->base;
if (XDGSURFACE->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) Debug::log(LOG, "New XDG Toplevel created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
return;
Debug::log(LOG, "New XDG Surface created. (class: {})", XDGSURFACE->toplevel->app_id ? XDGSURFACE->toplevel->app_id : "null");
const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get(); const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE; PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window"); PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->surface->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XDG Window"); PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_commitWindow.initCallback(&XDGSURFACE->surface->events.commit, &Events::listener_commitWindow, PNEWWINDOW, "XDG Window");
} }
void Events::listener_NewXDGDeco(wl_listener* listener, void* data) { void Events::listener_NewXDGDeco(wl_listener* listener, void* data) {
@ -1173,7 +1221,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg); wlr_xdg_surface_schedule_configure(PWINDOW->m_uSurface.xdg);
} else { } else {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1) if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1)
return; return;
g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED); g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED);
@ -1186,7 +1234,7 @@ void Events::listener_requestMinimize(void* owner, void* data) {
Debug::log(LOG, "Minimize request for {}", PWINDOW); Debug::log(LOG, "Minimize request for {}", PWINDOW);
if (PWINDOW->m_bIsX11) { if (PWINDOW->m_bIsX11) {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1) if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1)
return; return;
const auto E = (wlr_xwayland_minimize_event*)data; const auto E = (wlr_xwayland_minimize_event*)data;

View file

@ -6,17 +6,16 @@
#include "Vector2D.hpp" #include "Vector2D.hpp"
#include "Color.hpp" #include "Color.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include "../debug/Log.hpp"
enum ANIMATEDVARTYPE enum ANIMATEDVARTYPE {
{
AVARTYPE_INVALID = -1, AVARTYPE_INVALID = -1,
AVARTYPE_FLOAT, AVARTYPE_FLOAT,
AVARTYPE_VECTOR, AVARTYPE_VECTOR,
AVARTYPE_COLOR AVARTYPE_COLOR
}; };
enum AVARDAMAGEPOLICY enum AVARDAMAGEPOLICY {
{
AVARDAMAGE_NONE = -1, AVARDAMAGE_NONE = -1,
AVARDAMAGE_ENTIRE = 0, AVARDAMAGE_ENTIRE = 0,
AVARDAMAGE_BORDER, AVARDAMAGE_BORDER,
@ -36,10 +35,10 @@ class CAnimatedVariable {
void create(ANIMATEDVARTYPE, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY); void create(ANIMATEDVARTYPE, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
void create(ANIMATEDVARTYPE, std::any val, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY); void create(ANIMATEDVARTYPE, std::any val, SAnimationPropertyConfig*, void* pWindow, AVARDAMAGEPOLICY);
CAnimatedVariable(const CAnimatedVariable&) = delete; CAnimatedVariable(const CAnimatedVariable&) = delete;
CAnimatedVariable(CAnimatedVariable&&) = delete; CAnimatedVariable(CAnimatedVariable&&) = delete;
CAnimatedVariable& operator=(const CAnimatedVariable&) = delete; CAnimatedVariable& operator=(const CAnimatedVariable&) = delete;
CAnimatedVariable& operator=(CAnimatedVariable&&) = delete; CAnimatedVariable& operator=(CAnimatedVariable&&) = delete;
~CAnimatedVariable(); ~CAnimatedVariable();

129
src/helpers/Box.cpp Normal file
View file

@ -0,0 +1,129 @@
#include "Box.hpp"
wlr_box CBox::wlr() {
CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h};
return m_bWlrBox;
}
wlr_box* CBox::pWlr() {
CBox rounded = roundInternal();
m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h};
return &m_bWlrBox;
}
CBox& CBox::scale(double scale) {
x *= scale;
y *= scale;
w *= scale;
h *= scale;
return *this;
}
CBox& CBox::scale(const Vector2D& scale) {
x *= scale.x;
y *= scale.y;
w *= scale.x;
h *= scale.y;
return *this;
}
CBox& CBox::translate(const Vector2D& vec) {
x += vec.x;
y += vec.y;
return *this;
}
Vector2D CBox::middle() const {
return Vector2D{x + w / 2.0, y + h / 2.0};
}
bool CBox::containsPoint(const Vector2D& vec) const {
return VECINRECT(vec, x, y, x + w, y + h);
}
bool CBox::empty() const {
return w == 0 || h == 0;
}
CBox& CBox::applyFromWlr() {
x = m_bWlrBox.x;
y = m_bWlrBox.y;
w = m_bWlrBox.width;
h = m_bWlrBox.height;
return *this;
}
CBox& CBox::round() {
float newW = x + w - std::round(x);
float newH = y + h - std::round(y);
x = std::round(x);
y = std::round(y);
w = std::round(newW);
h = std::round(newH);
return *this;
}
CBox& CBox::transform(const wl_output_transform t, double w, double h) {
wlr_box_transform(&m_bWlrBox, pWlr(), t, w, h);
applyFromWlr();
return *this;
}
CBox& CBox::addExtents(const SWindowDecorationExtents& e) {
x -= e.topLeft.x;
y -= e.topLeft.y;
w += e.topLeft.x + e.bottomRight.x;
h += e.topLeft.y + e.bottomRight.y;
return *this;
}
CBox& CBox::scaleFromCenter(double scale) {
double oldW = w, oldH = h;
w *= scale;
h *= scale;
x -= (w - oldW) / 2.0;
y -= (h - oldH) / 2.0;
return *this;
}
CBox& CBox::expand(const double& value) {
x -= value;
y -= value;
w += value * 2.0;
h += value * 2.0;
return *this;
}
CBox CBox::roundInternal() {
float newW = x + w - std::floor(x);
float newH = y + h - std::floor(y);
return CBox{std::floor(x), std::floor(y), std::floor(newW), std::floor(newH)};
}
CBox CBox::copy() const {
return CBox{*this};
}
Vector2D CBox::pos() const {
return {x, y};
}
Vector2D CBox::size() const {
return {w, h};
}
SWindowDecorationExtents CBox::extentsFrom(const CBox& small) {
return {{small.x - x, small.y - y}, {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
}

87
src/helpers/Box.hpp Normal file
View file

@ -0,0 +1,87 @@
#pragma once
#include "Vector2D.hpp"
#include "../SharedDefs.hpp"
#include "../includes.hpp"
class CBox {
public:
CBox(double x_, double y_, double w_, double h_) {
x = x_;
y = y_;
w = w_;
h = h_;
}
CBox() {
w = 0;
h = 0;
}
CBox(const wlr_box& box) {
x = box.x;
y = box.y;
w = box.width;
h = box.height;
}
CBox(const double d) {
x = d;
y = d;
w = d;
h = d;
}
CBox(const Vector2D& pos, const Vector2D& size) {
x = pos.x;
y = pos.y;
w = size.x;
h = size.y;
}
wlr_box wlr();
wlr_box* pWlr();
CBox& applyFromWlr();
CBox& scale(double scale);
CBox& scaleFromCenter(double scale);
CBox& scale(const Vector2D& scale);
CBox& translate(const Vector2D& vec);
CBox& round();
CBox& transform(const wl_output_transform t, double w, double h);
CBox& addExtents(const SWindowDecorationExtents& e);
CBox& expand(const double& value);
CBox copy() const;
SWindowDecorationExtents extentsFrom(const CBox&); // this is the big box
Vector2D middle() const;
Vector2D pos() const;
Vector2D size() const;
bool containsPoint(const Vector2D& vec) const;
bool empty() const;
double x = 0, y = 0;
union {
double w;
double width;
};
union {
double h;
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;
}
private:
CBox roundInternal();
wlr_box m_bWlrBox;
};

View file

@ -5,7 +5,7 @@
class CColor { class CColor {
public: public:
CColor(); CColor();
CColor(float, float, float, float); CColor(float r, float g, float b, float a);
CColor(uint64_t); CColor(uint64_t);
float r = 0, g = 0, b = 0, a = 1.f; float r = 0, g = 0, b = 0, a = 1.f;
@ -27,4 +27,8 @@ class CColor {
bool operator==(const CColor& c2) const { bool operator==(const CColor& c2) const {
return r == c2.r && g == c2.g && b == c2.b && a == c2.a; return r == c2.r && g == c2.g && b == c2.b && a == c2.a;
} }
CColor stripA() const {
return {r, g, b, 1};
}
}; };

View file

@ -2,11 +2,14 @@
#include "../defines.hpp" #include "../defines.hpp"
#include <algorithm> #include <algorithm>
#include "../Compositor.hpp" #include "../Compositor.hpp"
#include <optional>
#include <set> #include <set>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#ifdef HAS_EXECINFO
#include <execinfo.h> #include <execinfo.h>
#endif
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h> #include <sys/sysctl.h>
@ -182,13 +185,6 @@ std::string escapeJSONStrings(const std::string& str) {
return oss.str(); return oss.str();
} }
void scaleBox(wlr_box* box, float scale) {
box->width = std::round(box->width * scale);
box->height = std::round(box->height * scale);
box->x = std::round(box->x * scale);
box->y = std::round(box->y * scale);
}
std::string removeBeginEndSpacesTabs(std::string str) { std::string removeBeginEndSpacesTabs(std::string str) {
if (str.empty()) if (str.empty())
return str; return str;
@ -208,12 +204,12 @@ std::string removeBeginEndSpacesTabs(std::string str) {
return str; return str;
} }
float getPlusMinusKeywordResult(std::string source, float relative) { std::optional<float> getPlusMinusKeywordResult(std::string source, float relative) {
try { try {
return relative + stof(source); return relative + stof(source);
} catch (...) { } catch (...) {
Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source); Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source);
return INT_MAX; return {};
} }
} }
@ -246,9 +242,13 @@ bool isDirection(const std::string& arg) {
return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b"; return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b";
} }
bool isDirection(const char& arg) {
return arg == 'l' || arg == 'r' || arg == 'u' || arg == 'd' || arg == 't' || arg == 'b';
}
int getWorkspaceIDFromString(const std::string& in, std::string& outName) { int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
int result = INT_MAX; int result = WORKSPACE_INVALID;
if (in.find("special") == 0) { if (in.starts_with("special")) {
outName = "special"; outName = "special";
if (in.length() > 8) { if (in.length() > 8) {
@ -262,7 +262,7 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
} }
return SPECIAL_WORKSPACE_START; return SPECIAL_WORKSPACE_START;
} else if (in.find("name:") == 0) { } else if (in.starts_with("name:")) {
const auto WORKSPACENAME = in.substr(in.find_first_of(':') + 1); const auto WORKSPACENAME = in.substr(in.find_first_of(':') + 1);
const auto WORKSPACE = g_pCompositor->getWorkspaceByName(WORKSPACENAME); const auto WORKSPACE = g_pCompositor->getWorkspaceByName(WORKSPACENAME);
if (!WORKSPACE) { if (!WORKSPACE) {
@ -271,26 +271,26 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
result = WORKSPACE->m_iID; result = WORKSPACE->m_iID;
} }
outName = WORKSPACENAME; outName = WORKSPACENAME;
} else if (in.find("empty") == 0) { } else if (in.starts_with("empty")) {
int id = 0; int id = 0;
while (++id < INT_MAX) { while (++id < INT_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0)) if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0))
return id; return id;
} }
} else if (in.find("prev") == 0) { } else if (in.starts_with("prev")) {
if (!g_pCompositor->m_pLastMonitor) if (!g_pCompositor->m_pLastMonitor)
return INT_MAX; return WORKSPACE_INVALID;
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
if (!PWORKSPACE) if (!PWORKSPACE)
return INT_MAX; return WORKSPACE_INVALID;
const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->m_sPrevWorkspace.iID); const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->m_sPrevWorkspace.iID);
if (!PLASTWORKSPACE) if (!PLASTWORKSPACE)
return INT_MAX; return WORKSPACE_INVALID;
outName = PLASTWORKSPACE->m_szName; outName = PLASTWORKSPACE->m_szName;
return PLASTWORKSPACE->m_iID; return PLASTWORKSPACE->m_iID;
@ -298,10 +298,15 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (in[0] == 'r' && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) { if (in[0] == 'r' && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
if (!g_pCompositor->m_pLastMonitor) { if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Relative monitor workspace on monitor null!"); Debug::log(ERR, "Relative monitor workspace on monitor null!");
result = INT_MAX; return WORKSPACE_INVALID;
return result;
} }
result = (int)getPlusMinusKeywordResult(in.substr(1), 0);
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = (int)PLUSMINUSRESULT.value();
int remains = (int)result; int remains = (int)result;
@ -389,12 +394,12 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
int beginID = finalWSID; int beginID = finalWSID;
int curID = finalWSID; int curID = finalWSID;
while (--curID > 0 && remainingWSes > 0) { while (--curID > 0 && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) { if (!invalidWSes.contains(curID)) {
remainingWSes--; remainingWSes--;
} }
finalWSID = curID; finalWSID = curID;
} }
if (finalWSID <= 0 || invalidWSes.find(finalWSID) != invalidWSes.end()) { if (finalWSID <= 0 || invalidWSes.contains(finalWSID)) {
if (namedWSes.size()) { if (namedWSes.size()) {
// Go to the named workspaces // Go to the named workspaces
// Need remainingWSes more // Need remainingWSes more
@ -414,7 +419,7 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (walkDir == '+') { if (walkDir == '+') {
int curID = finalWSID; int curID = finalWSID;
while (++curID < INT32_MAX && remainingWSes > 0) { while (++curID < INT32_MAX && remainingWSes > 0) {
if (invalidWSes.find(curID) == invalidWSes.end()) { if (!invalidWSes.contains(curID)) {
remainingWSes--; remainingWSes--;
} }
finalWSID = curID; finalWSID = curID;
@ -433,12 +438,16 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
if (!g_pCompositor->m_pLastMonitor) { if (!g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "Relative monitor workspace on monitor null!"); Debug::log(ERR, "Relative monitor workspace on monitor null!");
result = INT_MAX; return WORKSPACE_INVALID;
return result;
} }
// monitor relative // monitor relative
result = (int)getPlusMinusKeywordResult(in.substr(1), 0); const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = (int)PLUSMINUSRESULT.value();
// result now has +/- what we should move on mon // result now has +/- what we should move on mon
int remains = (int)result; int remains = (int)result;
@ -479,11 +488,15 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
outName = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName; outName = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName;
} else { } else {
if (in[0] == '+' || in[0] == '-') { if (in[0] == '+' || in[0] == '-') {
if (g_pCompositor->m_pLastMonitor) if (g_pCompositor->m_pLastMonitor) {
result = std::max((int)getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace), 1); const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace);
else { if (!PLUSMINUSRESULT.has_value())
return WORKSPACE_INVALID;
result = std::max((int)PLUSMINUSRESULT.value(), 1);
} else {
Debug::log(ERR, "Relative workspace on no mon!"); Debug::log(ERR, "Relative workspace on no mon!");
result = INT_MAX; return WORKSPACE_INVALID;
} }
} else if (isNumber(in)) } else if (isNumber(in))
result = std::max(std::stoi(in), 1); result = std::max(std::stoi(in), 1);
@ -501,6 +514,43 @@ int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
return result; return result;
} }
std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceName, std::string dirtyCmd) {
std::string cmd = removeBeginEndSpacesTabs(dirtyCmd);
if (!cmd.empty()) {
std::string rules;
const std::string workspaceRule = "workspace " + inWorkspaceName;
if (cmd[0] == '[') {
const int closingBracketIdx = cmd.find_last_of(']');
auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
cmd = cmd.substr(closingBracketIdx + 1);
auto rulesList = CVarList(tmpRules, 0, ';');
bool hadWorkspaceRule = false;
rulesList.map([&](std::string& rule) {
if (rule.find("workspace") == 0) {
rule = workspaceRule;
hadWorkspaceRule = true;
}
});
if (!hadWorkspaceRule)
rulesList.append(workspaceRule);
rules = "[" + rulesList.join(";") + "]";
} else {
rules = "[" + workspaceRule + "]";
}
return std::optional<std::string>(rules + " " + cmd);
}
return std::nullopt;
}
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2) { float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2) {
const float DX = std::max({0.0, p1.x - vec.x, vec.x - p2.x}); const float DX = std::max({0.0, p1.x - vec.x, vec.x - p2.x});
const float DY = std::max({0.0, p1.y - vec.y, vec.y - p2.y}); const float DY = std::max({0.0, p1.y - vec.y, vec.y - p2.y});
@ -527,10 +577,10 @@ void logSystemInfo() {
uname(&unameInfo); uname(&unameInfo);
Debug::log(LOG, "System name: {}", unameInfo.sysname); Debug::log(LOG, "System name: {}", std::string{unameInfo.sysname});
Debug::log(LOG, "Node name: {}", unameInfo.nodename); Debug::log(LOG, "Node name: {}", std::string{unameInfo.nodename});
Debug::log(LOG, "Release: {}", unameInfo.release); Debug::log(LOG, "Release: {}", std::string{unameInfo.release});
Debug::log(LOG, "Version: {}", unameInfo.version); Debug::log(LOG, "Version: {}", std::string{unameInfo.version});
Debug::log(NONE, "\n"); Debug::log(NONE, "\n");
@ -626,11 +676,11 @@ int64_t getPPIDof(int64_t pid) {
} }
int64_t configStringToInt(const std::string& VALUE) { int64_t configStringToInt(const std::string& VALUE) {
if (VALUE.find("0x") == 0) { if (VALUE.starts_with("0x")) {
// Values with 0x are hex // Values with 0x are hex
const auto VALUEWITHOUTHEX = VALUE.substr(2); const auto VALUEWITHOUTHEX = VALUE.substr(2);
return stol(VALUEWITHOUTHEX, nullptr, 16); return stol(VALUEWITHOUTHEX, nullptr, 16);
} else if (VALUE.find("rgba(") == 0 && VALUE.find(')') == VALUE.length() - 1) { } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6); const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) { if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) {
@ -642,7 +692,7 @@ int64_t configStringToInt(const std::string& VALUE) {
// now we need to RGBA -> ARGB. The config holds ARGB only. // now we need to RGBA -> ARGB. The config holds ARGB only.
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF); return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
} else if (VALUE.find("rgb(") == 0 && VALUE.find(')') == VALUE.length() - 1) { } else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5); const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) { if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) {
@ -653,11 +703,15 @@ int64_t configStringToInt(const std::string& VALUE) {
const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16); const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
return RGB + 0xFF000000; // 0xFF for opaque return RGB + 0xFF000000; // 0xFF for opaque
} else if (VALUE.find("true") == 0 || VALUE.find("on") == 0 || VALUE.find("yes") == 0) { } else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
return 1; return 1;
} else if (VALUE.find("false") == 0 || VALUE.find("off") == 0 || VALUE.find("no") == 0) { } else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
return 0; return 0;
} }
if (VALUE.empty() || !isNumber(VALUE))
return 0;
return std::stoll(VALUE); return std::stoll(VALUE);
} }
@ -689,9 +743,10 @@ std::string replaceInString(std::string subject, const std::string& search, cons
std::vector<SCallstackFrameInfo> getBacktrace() { std::vector<SCallstackFrameInfo> getBacktrace() {
std::vector<SCallstackFrameInfo> callstack; std::vector<SCallstackFrameInfo> callstack;
void* bt[1024]; #ifdef HAS_EXECINFO
size_t btSize; void* bt[1024];
char** btSymbols; size_t btSize;
char** btSymbols;
btSize = backtrace(bt, 1024); btSize = backtrace(bt, 1024);
btSymbols = backtrace_symbols(bt, btSize); btSymbols = backtrace_symbols(bt, btSize);
@ -699,6 +754,9 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
for (size_t i = 0; i < btSize; ++i) { for (size_t i = 0; i < btSize; ++i) {
callstack.emplace_back(SCallstackFrameInfo{bt[i], std::string{btSymbols[i]}}); callstack.emplace_back(SCallstackFrameInfo{bt[i], std::string{btSymbols[i]}});
} }
#else
callstack.emplace_back(SCallstackFrameInfo{nullptr, "configuration does not support execinfo.h"});
#endif
return callstack; return callstack;
} }
@ -706,4 +764,38 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
void throwError(const std::string& err) { void throwError(const std::string& err) {
Debug::log(CRIT, "Critical error thrown: {}", err); Debug::log(CRIT, "Critical error thrown: {}", err);
throw std::runtime_error(err); throw std::runtime_error(err);
}
uint32_t drmFormatToGL(uint32_t drm) {
switch (drm) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
#ifdef GLES2
return GL_RGB10_A2_EXT;
#else
return GL_RGB10_A2;
#endif
default: return GL_RGBA;
}
UNREACHABLE();
return GL_RGBA;
}
uint32_t glFormatToType(uint32_t gl) {
return gl != GL_RGBA ?
#ifdef GLES2
GL_UNSIGNED_INT_2_10_10_10_REV_EXT :
#else
GL_UNSIGNED_INT_2_10_10_10_REV :
#endif
GL_UNSIGNED_BYTE;
}
bool envEnabled(const std::string& env) {
const auto ENV = getenv(env.c_str());
if (!ENV)
return false;
return std::string(ENV) == "1";
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <optional>
#include <string> #include <string>
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/util/box.h> #include <wlr/util/box.h>
@ -15,22 +16,26 @@ struct SCallstackFrameInfo {
std::string absolutePath(const std::string&, const std::string&); std::string absolutePath(const std::string&, const std::string&);
void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString); void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString);
std::string escapeJSONStrings(const std::string& str); std::string escapeJSONStrings(const std::string& str);
void scaleBox(wlr_box*, float);
std::string removeBeginEndSpacesTabs(std::string); std::string removeBeginEndSpacesTabs(std::string);
bool isNumber(const std::string&, bool allowfloat = false); bool isNumber(const std::string&, bool allowfloat = false);
bool isDirection(const std::string&); bool isDirection(const std::string&);
bool isDirection(const char&);
int getWorkspaceIDFromString(const std::string&, std::string&); int getWorkspaceIDFromString(const std::string&, std::string&);
std::optional<std::string> cleanCmdForWorkspace(const std::string&, std::string);
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2);
void logSystemInfo(); void logSystemInfo();
std::string execAndGet(const char*); std::string execAndGet(const char*);
int64_t getPPIDof(int64_t pid); int64_t getPPIDof(int64_t pid);
int64_t configStringToInt(const std::string&); int64_t configStringToInt(const std::string&);
float getPlusMinusKeywordResult(std::string in, float relative); std::optional<float> getPlusMinusKeywordResult(std::string in, float relative);
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr); void matrixProjection(float mat[9], int w, int h, wl_output_transform tr);
double normalizeAngleRad(double ang); double normalizeAngleRad(double ang);
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace); std::string replaceInString(std::string subject, const std::string& search, const std::string& replace);
std::vector<SCallstackFrameInfo> getBacktrace(); std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err); void throwError(const std::string& err);
uint32_t drmFormatToGL(uint32_t drm);
uint32_t glFormatToType(uint32_t gl);
bool envEnabled(const std::string& env);
template <typename... Args> template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) { [[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View file

@ -40,6 +40,8 @@ void CMonitor::onConnect(bool noRule) {
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this); hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this);
hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this); hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this);
tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
if (m_bEnabled) { if (m_bEnabled) {
wlr_output_enable(output, 1); wlr_output_enable(output, 1);
wlr_output_commit(output); wlr_output_commit(output);
@ -48,6 +50,10 @@ void CMonitor::onConnect(bool noRule) {
szName = output->name; szName = output->name;
szDescription = output->description ? output->description : "";
// 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());
if (!wlr_backend_is_drm(output->backend)) if (!wlr_backend_is_drm(output->backend))
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable
@ -107,20 +113,20 @@ void CMonitor::onConnect(bool noRule) {
m_bRenderingInitPassed = true; m_bRenderingInitPassed = true;
} }
if (!m_pThisWrap) { std::shared_ptr<CMonitor>* thisWrapper = nullptr;
// find the wrap // find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) { if (m->ID == ID) {
m_pThisWrap = &m; thisWrapper = &m;
break; break;
}
} }
} }
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) { RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???");
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap);
} if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end())
g_pCompositor->m_vMonitors.push_back(*thisWrapper);
m_bEnabled = true; m_bEnabled = true;
@ -130,6 +136,8 @@ void CMonitor::onConnect(bool noRule) {
if (!noRule) if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
wlr_output_commit(output);
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y); wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale); wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
@ -150,8 +158,6 @@ void CMonitor::onConnect(bool noRule) {
if (scale < 0.1) if (scale < 0.1)
scale = getDefaultScale(); scale = getDefaultScale();
m_pThisWrap = nullptr;
forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering.
// //
@ -182,9 +188,11 @@ void CMonitor::onConnect(bool noRule) {
g_pCompositor->setActiveMonitor(this); g_pCompositor->setActiveMonitor(this);
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this);
} }
void CMonitor::onDisconnect() { void CMonitor::onDisconnect(bool destroy) {
if (renderTimer) { if (renderTimer) {
wl_event_source_remove(renderTimer); wl_event_source_remove(renderTimer);
@ -219,9 +227,6 @@ void CMonitor::onDisconnect() {
g_pConfigManager->m_bWantsMonitorReload = true; g_pConfigManager->m_bWantsMonitorReload = true;
} }
m_bEnabled = false;
m_bRenderingInitPassed = false;
hyprListener_monitorFrame.removeCallback(); hyprListener_monitorFrame.removeCallback();
hyprListener_monitorDamage.removeCallback(); hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback(); hyprListener_monitorNeedsFrame.removeCallback();
@ -246,6 +251,9 @@ void CMonitor::onDisconnect() {
g_pCompositor->enterUnsafeState(); g_pCompositor->enterUnsafeState();
} }
m_bEnabled = false;
m_bRenderingInitPassed = false;
if (BACKUPMON) { if (BACKUPMON) {
// snap cursor // snap cursor
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f, wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, BACKUPMON->vecPosition.x + BACKUPMON->vecTransformedSize.x / 2.f,
@ -254,7 +262,7 @@ void CMonitor::onDisconnect() {
// move workspaces // move workspaces
std::deque<CWorkspace*> wspToMove; std::deque<CWorkspace*> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) { if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) {
wspToMove.push_back(w.get()); wspToMove.push_back(w.get());
} }
} }
@ -272,7 +280,8 @@ void CMonitor::onDisconnect() {
activeWorkspace = -1; activeWorkspace = -1;
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output); if (!destroy)
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output);
wlr_output_enable(output, false); wlr_output_enable(output, false);
@ -294,7 +303,6 @@ void CMonitor::onDisconnect() {
g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz; g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz;
} }
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; }); std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
} }
@ -313,14 +321,14 @@ void CMonitor::addDamage(const CRegion* rg) {
addDamage(const_cast<CRegion*>(rg)->pixman()); addDamage(const_cast<CRegion*>(rg)->pixman());
} }
void CMonitor::addDamage(const wlr_box* box) { void CMonitor::addDamage(const CBox* box) {
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue; static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
wlr_damage_ring_add_whole(&damage); wlr_damage_ring_add_whole(&damage);
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this);
} }
if (wlr_damage_ring_add_box(&damage, box)) if (wlr_damage_ring_add_box(&damage, const_cast<CBox*>(box)->pWlr()))
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this);
} }
@ -349,7 +357,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
findAvailableDefaultWS() : findAvailableDefaultWS() :
getWorkspaceIDFromString(g_pConfigManager->getDefaultWorkspaceFor(szName), newDefaultWorkspaceName); getWorkspaceIDFromString(g_pConfigManager->getDefaultWorkspaceFor(szName), newDefaultWorkspaceName);
if (WORKSPACEID == INT_MAX || (WORKSPACEID >= SPECIAL_WORKSPACE_START && WORKSPACEID <= -2)) { if (WORKSPACEID == WORKSPACE_INVALID || (WORKSPACEID >= SPECIAL_WORKSPACE_START && WORKSPACEID <= -2)) {
WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1; WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1;
newDefaultWorkspaceName = std::to_string(WORKSPACEID); newDefaultWorkspaceName = std::to_string(WORKSPACEID);
@ -412,19 +420,22 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
vecPosition = RULE.offset; vecPosition = RULE.offset;
// push to mvmonitors // push to mvmonitors
if (!m_pThisWrap) {
// find the wrap std::shared_ptr<CMonitor>* thisWrapper = nullptr;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) { // find the wrap
m_pThisWrap = &m; for (auto& m : g_pCompositor->m_vRealMonitors) {
break; if (m->ID == ID) {
} thisWrapper = &m;
break;
} }
} }
RASSERT(thisWrapper->get(), "CMonitor::setMirror: Had no wrapper???");
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) ==
g_pCompositor->m_vMonitors.end()) { g_pCompositor->m_vMonitors.end()) {
g_pCompositor->m_vMonitors.push_back(*m_pThisWrap); g_pCompositor->m_vMonitors.push_back(*thisWrapper);
} }
setupDefaultWS(RULE); setupDefaultWS(RULE);
@ -491,7 +502,7 @@ float CMonitor::getDefaultScale() {
return 1; 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) if (!pWorkspace)
return; return;
@ -522,24 +533,24 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool
} }
} }
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue; if (!noFocus && !g_pCompositor->m_pLastMonitor->specialWorkspaceID) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
CWindow* pWindow = pWorkspace->getLastFocusedWindow();
if (const auto PLASTWINDOW = pWorkspace->getLastFocusedWindow(); PLASTWINDOW) if (!pWindow) {
g_pCompositor->focusWindow(PLASTWINDOW); if (*PFOLLOWMOUSE == 1)
else { pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal());
CWindow* pWindow = nullptr;
if (*PFOLLOWMOUSE == 1) if (!pWindow)
pWindow = g_pCompositor->vectorToWindowIdeal(g_pInputManager->getMouseCoordsInternal()); pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow) if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID); pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
}
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
g_pCompositor->focusWindow(pWindow); g_pCompositor->focusWindow(pWindow);
} }
if (!noMouseMove) if (!noMouseMove)
g_pInputManager->simulateMouseMovement(); g_pInputManager->simulateMouseMovement();
@ -552,6 +563,10 @@ void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal, bool
g_pHyprRenderer->damageMonitor(this); g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace); g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pCompositor->updateSuspendedStates();
} }
void CMonitor::changeWorkspace(const int& id, bool internal) { void CMonitor::changeWorkspace(const int& id, bool internal) {
@ -577,6 +592,8 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
else else
g_pInputManager->refocus(); g_pInputManager->refocus();
g_pCompositor->updateSuspendedStates();
return; return;
} }
@ -604,7 +621,23 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) { if (w->m_iWorkspaceID == pWorkspace->m_iID) {
w->m_iMonitorID = ID; 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) {
// if it's floating and the middle isnt on the current mon, move it to the center
const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE);
Vector2D pos = w->m_vRealPosition.goalv();
if (!VECINRECT(MIDDLE, PMONFROMMIDDLE->vecPosition.x, PMONFROMMIDDLE->vecPosition.y, PMONFROMMIDDLE->vecPosition.x + PMONFROMMIDDLE->vecSize.x,
PMONFROMMIDDLE->vecPosition.y + PMONFROMMIDDLE->vecSize.y)) {
// not on any monitor, center
pos = middle() / 2.f - w->m_vRealSize.goalv() / 2.f;
} else
pos = pos - PMONFROMMIDDLE->vecPosition + vecPosition;
w->m_vRealPosition = pos;
w->m_vPosition = pos;
}
} }
} }
@ -618,6 +651,8 @@ void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName}); g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(this); g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateSuspendedStates();
} }
void CMonitor::setSpecialWorkspace(const int& id) { void CMonitor::setSpecialWorkspace(const int& id) {
@ -633,4 +668,13 @@ void CMonitor::moveTo(const Vector2D& pos) {
Vector2D CMonitor::middle() { Vector2D CMonitor::middle() {
return vecPosition + vecSize / 2.f; return vecPosition + vecSize / 2.f;
} }
void CMonitor::updateMatrix() {
wlr_matrix_identity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
wlr_matrix_transform(projMatrix.data(), transform);
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
}

View file

@ -40,9 +40,11 @@ class CMonitor {
uint64_t ID = -1; uint64_t ID = -1;
int activeWorkspace = -1; int activeWorkspace = -1;
float scale = 1; float setScale = 1; // scale set by cfg
float scale = 1; // real scale
std::string szName = ""; std::string szName = "";
std::string szDescription = "";
Vector2D vecReservedTopLeft = Vector2D(0, 0); Vector2D vecReservedTopLeft = Vector2D(0, 0);
Vector2D vecReservedBottomRight = Vector2D(0, 0); Vector2D vecReservedBottomRight = Vector2D(0, 0);
@ -50,30 +52,33 @@ class CMonitor {
drmModeModeInfo customDrmMode = {}; drmModeModeInfo customDrmMode = {};
// WLR stuff // WLR stuff
wlr_damage_ring damage; wlr_damage_ring damage;
wlr_output* output = nullptr; wlr_output* output = nullptr;
float refreshRate = 60; float refreshRate = 60;
int framesToSkip = 0; int framesToSkip = 0;
int forceFullFrames = 0; int forceFullFrames = 0;
bool noFrameSchedule = false; bool noFrameSchedule = false;
bool scheduledRecalc = false; bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
bool gammaChanged = false; bool gammaChanged = false;
float xwaylandScale = 1.f; float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0};
bool dpmsStatus = true; 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 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 enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false; 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 pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false; bool renderingActive = false;
wl_event_source* renderTimer = nullptr; // for RAT wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false; bool RATScheduled = false;
CTimer lastPresentationTimer; CTimer lastPresentationTimer;
SMonitorRule activeMonitorRule; SMonitorRule activeMonitorRule;
// mirroring // mirroring
CMonitor* pMirrorOf = nullptr; CMonitor* pMirrorOf = nullptr;
@ -81,6 +86,18 @@ class CMonitor {
CRegion lastFrameDamage; // stores last frame damage CRegion lastFrameDamage; // stores last frame damage
// for tearing
CWindow* solitaryClient = nullptr;
struct {
bool canTear = false;
bool nextRenderTorn = false;
bool activelyTearing = false;
bool busy = false;
bool frameScheduledWhileBusy = false;
} tearingState;
// for the special workspace. 0 means not open. // for the special workspace. 0 means not open.
int specialWorkspaceID = 0; int specialWorkspaceID = 0;
@ -95,24 +112,24 @@ class CMonitor {
DYNLISTENER(monitorBind); DYNLISTENER(monitorBind);
// methods // methods
void onConnect(bool noRule); void onConnect(bool noRule);
void onDisconnect(); void onDisconnect(bool destroy = false);
void addDamage(const pixman_region32_t* rg); void addDamage(const pixman_region32_t* rg);
void addDamage(const CRegion* rg); void addDamage(const CRegion* rg);
void addDamage(const wlr_box* box); void addDamage(const CBox* box);
void setMirror(const std::string&); void setMirror(const std::string&);
bool isMirror(); bool isMirror();
float getDefaultScale(); float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false); void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const int& id, bool internal = false); void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace); void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id); void setSpecialWorkspace(const int& id);
void moveTo(const Vector2D& pos); void moveTo(const Vector2D& pos);
Vector2D middle(); Vector2D middle();
void updateMatrix();
std::shared_ptr<CMonitor>* m_pThisWrap = nullptr; bool m_bEnabled = false;
bool m_bEnabled = false; bool m_bRenderingInitPassed = false;
bool m_bRenderingInitPassed = false;
// For the list lookup // For the list lookup

View file

@ -21,6 +21,10 @@ CRegion::CRegion(wlr_box* box) {
pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->width, box->height); pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->width, box->height);
} }
CRegion::CRegion(const CBox& box) {
pixman_region32_init_rect(&m_rRegion, box.x, box.y, box.w, box.h);
}
CRegion::CRegion(pixman_box32_t* box) { CRegion::CRegion(pixman_box32_t* box) {
pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1); pixman_region32_init_rect(&m_rRegion, box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1);
} }
@ -59,6 +63,11 @@ CRegion& CRegion::add(double x, double y, double w, double h) {
return *this; return *this;
} }
CRegion& CRegion::add(const CBox& other) {
pixman_region32_union_rect(&m_rRegion, &m_rRegion, other.x, other.y, other.w, other.h);
return *this;
}
CRegion& CRegion::subtract(const CRegion& other) { CRegion& CRegion::subtract(const CRegion& other) {
pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman()); pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
return *this; return *this;
@ -79,11 +88,25 @@ CRegion& CRegion::invert(pixman_box32_t* box) {
return *this; return *this;
} }
CRegion& CRegion::invert(const CBox& box) {
pixman_box32 pixmanBox = {box.x, box.y, box.w + box.x, box.h + box.y};
return this->invert(&pixmanBox);
}
CRegion& CRegion::translate(const Vector2D& vec) { CRegion& CRegion::translate(const Vector2D& vec) {
pixman_region32_translate(&m_rRegion, vec.x, vec.y); pixman_region32_translate(&m_rRegion, vec.x, vec.y);
return *this; return *this;
} }
CRegion& CRegion::transform(const wl_output_transform t, double w, double h) {
wlr_region_transform(&m_rRegion, &m_rRegion, t, w, h);
return *this;
}
CRegion CRegion::copy() const {
return CRegion(*this);
}
CRegion& CRegion::scale(float scale) { CRegion& CRegion::scale(float scale) {
wlr_region_scale(&m_rRegion, &m_rRegion, scale); wlr_region_scale(&m_rRegion, &m_rRegion, scale);
return *this; return *this;
@ -100,7 +123,7 @@ std::vector<pixman_box32_t> CRegion::getRects() const {
return result; return result;
} }
wlr_box CRegion::getExtents() { CBox CRegion::getExtents() {
pixman_box32_t* box = pixman_region32_extents(&m_rRegion); pixman_box32_t* box = pixman_region32_extents(&m_rRegion);
return {box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1}; return {box->x1, box->y1, box->x2 - box->x1, box->y2 - box->y1};
} }

View file

@ -2,6 +2,7 @@
#include <pixman.h> #include <pixman.h>
#include <vector> #include <vector>
#include "Vector2D.hpp" #include "Vector2D.hpp"
#include "Box.hpp"
struct wlr_box; struct wlr_box;
@ -15,6 +16,8 @@ class CRegion {
CRegion(double x, double y, double w, double h); CRegion(double x, double y, double w, double h);
/* Create from a wlr_box */ /* Create from a wlr_box */
CRegion(wlr_box* box); CRegion(wlr_box* box);
/* Create from a CBox */
CRegion(const CBox& box);
/* Create from a pixman_box32_t */ /* Create from a pixman_box32_t */
CRegion(pixman_box32_t* box); CRegion(pixman_box32_t* box);
@ -37,21 +40,25 @@ class CRegion {
CRegion& set(const CRegion& other); CRegion& set(const CRegion& other);
CRegion& add(const CRegion& other); CRegion& add(const CRegion& other);
CRegion& add(double x, double y, double w, double h); CRegion& add(double x, double y, double w, double h);
CRegion& add(const CBox& other);
CRegion& subtract(const CRegion& other); CRegion& subtract(const CRegion& other);
CRegion& intersect(const CRegion& other); CRegion& intersect(const CRegion& other);
CRegion& intersect(double x, double y, double w, double h); CRegion& intersect(double x, double y, double w, double h);
CRegion& translate(const Vector2D& vec); CRegion& translate(const Vector2D& vec);
CRegion& transform(const wl_output_transform t, double w, double h);
CRegion& invert(pixman_box32_t* box); CRegion& invert(pixman_box32_t* box);
CRegion& invert(const CBox& box);
CRegion& scale(float scale); CRegion& scale(float scale);
wlr_box getExtents(); CBox getExtents();
bool containsPoint(const Vector2D& vec) const; bool containsPoint(const Vector2D& vec) const;
bool empty() const; bool empty() const;
Vector2D closestPoint(const Vector2D& vec) const; Vector2D closestPoint(const Vector2D& vec) const;
CRegion copy() const;
std::vector<pixman_box32_t> getRects() const; std::vector<pixman_box32_t> getRects() const;
pixman_region32_t* pixman() { pixman_region32_t* pixman() {
return &m_rRegion; return &m_rRegion;
} }
private: private:

View file

@ -98,8 +98,9 @@ void SubsurfaceTree::destroySurfaceTree(SSurfaceTreeNode* pNode) {
// damage // damage
if (pNode->pSurface && pNode->pSurface->exists()) { if (pNode->pSurface && pNode->pSurface->exists()) {
wlr_box extents = {}; CBox extents = {};
wlr_surface_get_extends(pNode->pSurface->wlr(), &extents); wlr_surface_get_extends(pNode->pSurface->wlr(), extents.pWlr());
extents.applyFromWlr();
int lx = 0, ly = 0; int lx = 0, ly = 0;
addSurfaceGlobalOffset(pNode, &lx, &ly); addSurfaceGlobalOffset(pNode, &lx, &ly);
@ -177,6 +178,9 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface); Debug::log(LOG, "Subsurface {:x} mapped", (uintptr_t)subsurface->pSubsurface);
subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner); 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) { void Events::listener_unmapSubsurface(void* owner, void* data) {
@ -198,7 +202,7 @@ void Events::listener_unmapSubsurface(void* owner, void* data) {
int lx = 0, ly = 0; int lx = 0, ly = 0;
addSurfaceGlobalOffset(PNODE, &lx, &ly); addSurfaceGlobalOffset(PNODE, &lx, &ly);
wlr_box extents = {lx, ly, 0, 0}; CBox extents = {lx, ly, 0, 0};
extents.width = PNODE->pSurface->wlr()->current.width; extents.width = PNODE->pSurface->wlr()->current.width;
extents.height = PNODE->pSurface->wlr()->current.height; extents.height = PNODE->pSurface->wlr()->current.height;
@ -219,6 +223,8 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
// no damaging if it's not visible // no damaging if it's not visible
if (!g_pHyprRenderer->shouldRenderWindow(pNode->pWindowOwner)) { 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; static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue;
if (*PLOGDAMAGE) if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from {} because it's invisible.", pNode->pWindowOwner); Debug::log(LOG, "Refusing to commit damage from {} because it's invisible.", pNode->pWindowOwner);
@ -243,8 +249,37 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
} }
} }
if (pNode->pSurface && pNode->pSurface->exists()) if (pNode->pSurface && pNode->pSurface->exists()) {
g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly, SCALE); g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly, SCALE);
if (pNode->lastSize != Vector2D{pNode->pSurface->wlr()->current.width, pNode->pSurface->wlr()->current.height} && pNode->pWindowOwner)
g_pHyprRenderer->damageWindow(pNode->pWindowOwner);
}
if (pNode->pWindowOwner) {
if (pNode->pWindowOwner->m_bIsX11)
pNode->pWindowOwner->m_vReportedSize = pNode->pWindowOwner->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged.
// 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 &&
pNode->pSurface->wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) {
CRegion damageBox;
wlr_surface_get_effective_damage(pNode->pSurface->wlr(), damageBox.pixman());
if (!damageBox.empty()) {
if (PMONITOR->tearingState.busy) {
PMONITOR->tearingState.frameScheduledWhileBusy = true;
} else {
PMONITOR->tearingState.nextRenderTorn = true;
g_pHyprRenderer->renderMonitor(PMONITOR);
}
}
}
}
pNode->lastSize = pNode->pSurface->exists() ? Vector2D{pNode->pSurface->wlr()->current.width, pNode->pSurface->wlr()->current.height} : Vector2D{};
} }
void Events::listener_destroySubsurface(void* owner, void* data) { void Events::listener_destroySubsurface(void* owner, void* data) {

View file

@ -26,6 +26,8 @@ struct SSurfaceTreeNode {
void* globalOffsetData; void* globalOffsetData;
CWindow* pWindowOwner = nullptr; CWindow* pWindowOwner = nullptr;
Vector2D lastSize;
// //
bool operator==(const SSurfaceTreeNode& rhs) const { bool operator==(const SSurfaceTreeNode& rhs) const {
return pSurface == rhs.pSurface; return pSurface == rhs.pSurface;

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <functional>
#include <vector> #include <vector>
#include <string> #include <string>
#include "../macros.hpp" #include "../macros.hpp"
@ -20,6 +21,15 @@ class CVarList {
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const; std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
void map(std::function<void(std::string&)> func) {
for (auto& s : m_vArgs)
func(s);
}
void append(const std::string arg) {
m_vArgs.emplace_back(arg);
}
std::string operator[](const size_t& idx) const { std::string operator[](const size_t& idx) const {
if (idx >= m_vArgs.size()) if (idx >= m_vArgs.size())
return ""; return "";
@ -40,6 +50,15 @@ class CVarList {
return m_vArgs.end(); return m_vArgs.end();
} }
bool contains(const std::string& el) {
for (auto& a : m_vArgs) {
if (a == el)
return true;
}
return false;
}
private: private:
std::vector<std::string> m_vArgs; std::vector<std::string> m_vArgs;
}; };

View file

@ -28,6 +28,10 @@ Vector2D Vector2D::floor() const {
return Vector2D(std::floor(x), std::floor(y)); return Vector2D(std::floor(x), std::floor(y));
} }
Vector2D Vector2D::round() const {
return Vector2D(std::round(x), std::round(y));
}
Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const { Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y)); return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
} }
@ -45,3 +49,7 @@ bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1;
} }
double Vector2D::size() const {
return std::sqrt(x * x + y * y);
}

View file

@ -22,10 +22,13 @@ class Vector2D {
Vector2D operator-(const Vector2D& a) const { Vector2D operator-(const Vector2D& a) const {
return Vector2D(this->x - a.x, this->y - a.y); return Vector2D(this->x - a.x, this->y - a.y);
} }
Vector2D operator*(const float& a) const { Vector2D operator-() const {
return Vector2D(-this->x, -this->y);
}
Vector2D operator*(const double& a) const {
return Vector2D(this->x * a, this->y * a); return Vector2D(this->x * a, this->y * a);
} }
Vector2D operator/(const float& a) const { Vector2D operator/(const double& a) const {
return Vector2D(this->x / a, this->y / a); return Vector2D(this->x / a, this->y / a);
} }
@ -52,12 +55,43 @@ class Vector2D {
bool operator<(const Vector2D& a) const { bool operator<(const Vector2D& a) const {
return this->x < a.x && this->y < a.y; return this->x < a.x && this->y < a.y;
} }
Vector2D& operator+=(const Vector2D& a) {
this->x += a.x;
this->y += a.y;
return *this;
}
Vector2D& operator-=(const Vector2D& a) {
this->x -= a.x;
this->y -= a.y;
return *this;
}
Vector2D& operator*=(const Vector2D& a) {
this->x *= a.x;
this->y *= a.y;
return *this;
}
Vector2D& operator/=(const Vector2D& a) {
this->x /= a.x;
this->y /= a.y;
return *this;
}
Vector2D& operator*=(const double& a) {
this->x *= a;
this->y *= a;
return *this;
}
Vector2D& operator/=(const double& a) {
this->x /= a;
this->y /= a;
return *this;
}
double distance(const Vector2D& other) const; 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()) const;
Vector2D floor() const; Vector2D floor() const;
Vector2D round() const;
bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const; bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const;
}; };

View file

@ -8,6 +8,14 @@ SLayerSurface::SLayerSurface() {
alpha.registerVar(); alpha.registerVar();
} }
SLayerSurface::~SLayerSurface() {
if (!g_pHyprOpenGL)
return;
g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first == this; });
}
void SLayerSurface::applyRules() { void SLayerSurface::applyRules() {
noAnimations = false; noAnimations = false;
forceBlur = false; forceBlur = false;
@ -20,7 +28,7 @@ void SLayerSurface::applyRules() {
noAnimations = true; noAnimations = true;
else if (rule.rule == "blur") else if (rule.rule == "blur")
forceBlur = true; forceBlur = true;
else if (rule.rule.find("ignorealpha") == 0 || rule.rule.find("ignorezero") == 0) { else if (rule.rule.starts_with("ignorealpha") || rule.rule.starts_with("ignorezero")) {
const auto FIRST_SPACE_POS = rule.rule.find_first_of(' '); const auto FIRST_SPACE_POS = rule.rule.find_first_of(' ');
std::string alphaValue = ""; std::string alphaValue = "";
if (FIRST_SPACE_POS != std::string::npos) if (FIRST_SPACE_POS != std::string::npos)
@ -31,7 +39,7 @@ void SLayerSurface::applyRules() {
if (!alphaValue.empty()) if (!alphaValue.empty())
ignoreAlphaValue = std::stof(alphaValue); ignoreAlphaValue = std::stof(alphaValue);
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); } } catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
} else if (rule.rule.find("xray") == 0) { } else if (rule.rule.starts_with("xray")) {
CVarList vars{rule.rule, 0, ' '}; CVarList vars{rule.rule, 0, ' '};
try { try {
xray = configStringToInt(vars[1]); xray = configStringToInt(vars[1]);

View file

@ -16,6 +16,7 @@ struct SLayerRule {
struct SLayerSurface { struct SLayerSurface {
SLayerSurface(); SLayerSurface();
~SLayerSurface();
void applyRules(); void applyRules();
@ -33,7 +34,7 @@ struct SLayerSurface {
DYNLISTENER(commitLayerSurface); DYNLISTENER(commitLayerSurface);
DYNLISTENER(newPopup); DYNLISTENER(newPopup);
wlr_box geometry = {0, 0, 0, 0}; CBox geometry = {0, 0, 0, 0};
Vector2D position; Vector2D position;
zwlr_layer_shell_v1_layer layer; zwlr_layer_shell_v1_layer layer;
@ -65,12 +66,12 @@ class CMonitor;
struct SRenderData { struct SRenderData {
CMonitor* pMonitor; CMonitor* pMonitor;
timespec* when; timespec* when;
int x, y; double x, y;
// for iters // for iters
void* data = nullptr; void* data = nullptr;
wlr_surface* surface = nullptr; wlr_surface* surface = nullptr;
int w, h; double w, h;
// for rounding // for rounding
bool dontRound = true; bool dontRound = true;
@ -96,6 +97,8 @@ struct SRenderData {
// for calculating UV // for calculating UV
CWindow* pWindow = nullptr; CWindow* pWindow = nullptr;
bool popup = false;
}; };
struct SExtensionFindingData { struct SExtensionFindingData {
@ -168,18 +171,20 @@ struct SConstraint {
bool active = false; bool active = false;
bool hintSet = false; bool hintSet = false;
Vector2D positionHint = {-1, -1}; // the position hint, but will be set to the current cursor pos if not set. Vector2D positionHint = {-1, -1}; // the position hint, but will use cursorPosOnActivate if unset
Vector2D cursorPosOnActivate = {-1, -1};
DYNLISTENER(setConstraintRegion); DYNLISTENER(setConstraintRegion);
DYNLISTENER(destroyConstraint); DYNLISTENER(destroyConstraint);
CRegion getLogicCoordsRegion(); CRegion getLogicCoordsRegion();
Vector2D getLogicConstraintPos(); Vector2D getLogicConstraintPos();
Vector2D getLogicConstraintSize(); Vector2D getLogicConstraintSize();
bool operator==(const SConstraint& b) const { //
return constraint == b.constraint; bool operator==(const SConstraint& b) const {
return constraint == b.constraint;
} }
}; };
@ -250,6 +255,8 @@ struct STablet {
wlr_tablet_v2_tablet* wlrTabletV2 = nullptr; wlr_tablet_v2_tablet* wlrTabletV2 = nullptr;
wlr_input_device* wlrDevice = nullptr; wlr_input_device* wlrDevice = nullptr;
bool relativeInput = false;
std::string name = ""; std::string name = "";
// //
@ -392,3 +399,14 @@ struct SSwitchDevice {
return pWlrDevice == other.pWlrDevice; return pWlrDevice == other.pWlrDevice;
} }
}; };
struct STearingController {
wlr_tearing_control_v1* pWlrHint = nullptr;
DYNLISTENER(set);
DYNLISTENER(destroy);
bool operator==(const STearingController& other) {
return pWlrHint == other.pWlrHint;
}
};

View file

@ -2,11 +2,18 @@
#include "MiscFunctions.hpp" #include "MiscFunctions.hpp"
#include <string> #include <string>
#include "../debug/Log.hpp" #include "../debug/Log.hpp"
#include "Watchdog.hpp"
void handleWrapped(wl_listener* listener, void* data) { void handleWrapped(wl_listener* listener, void* data) {
CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener);
pWrap->m_pSelf->emit(data); 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();
} }
CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function<void(void*, void*)> callback, void* pOwner) { CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function<void(void*, void*)> callback, void* pOwner) {
@ -50,4 +57,4 @@ void CHyprWLListener::initCallback(wl_signal* pSignal, std::function<void(void*,
void CHyprWLListener::emit(void* data) { void CHyprWLListener::emit(void* data) {
m_pCallback(m_pOwner, data); m_pCallback(m_pOwner, data);
} }

View file

@ -27,17 +27,45 @@ wlr_surface* CWLSurface::wlr() const {
return m_pWLRSurface; return m_pWLRSurface;
} }
bool CWLSurface::small() const {
if (!m_pOwner || !exists())
return false;
return m_pOwner->m_vReportedSize.x > m_pWLRSurface->current.buffer_width + 1 || m_pOwner->m_vReportedSize.y > m_pWLRSurface->current.buffer_height + 1;
}
Vector2D CWLSurface::correctSmallVec() const {
if (!m_pOwner || !exists() || !small() || m_bFillIgnoreSmall)
return {};
const auto SIZE = getViewporterCorrectedSize();
return Vector2D{(m_pOwner->m_vReportedSize.x - SIZE.x) / 2, (m_pOwner->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) *
(m_pOwner->m_vRealSize.vec() / m_pOwner->m_vReportedSize);
}
Vector2D CWLSurface::getViewporterCorrectedSize() const {
if (!exists())
return {};
return m_pWLRSurface->current.viewport.has_dst ? Vector2D{m_pWLRSurface->current.viewport.dst_width, m_pWLRSurface->current.viewport.dst_height} :
Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height};
}
void CWLSurface::destroy() { void CWLSurface::destroy() {
if (!m_pWLRSurface) if (!m_pWLRSurface)
return; return;
hyprListener_destroy.removeCallback(); hyprListener_destroy.removeCallback();
m_pWLRSurface->data = nullptr; m_pWLRSurface->data = nullptr;
m_pOwner = nullptr;
if (g_pCompositor->m_pLastFocus == m_pWLRSurface) if (g_pCompositor->m_pLastFocus == m_pWLRSurface)
g_pCompositor->m_pLastFocus = nullptr; g_pCompositor->m_pLastFocus = nullptr;
if (g_pInputManager->m_pLastMouseSurface == m_pWLRSurface) if (g_pInputManager->m_pLastMouseSurface == m_pWLRSurface)
g_pInputManager->m_pLastMouseSurface = nullptr; g_pInputManager->m_pLastMouseSurface = nullptr;
if (g_pHyprRenderer->m_sLastCursorData.surf == m_pWLRSurface)
g_pHyprRenderer->m_sLastCursorData.surf.reset();
m_pWLRSurface = nullptr; m_pWLRSurface = nullptr;

View file

@ -1,6 +1,9 @@
#pragma once #pragma once
#include "../defines.hpp" #include "../defines.hpp"
class CWindow;
class CWLSurface { class CWLSurface {
public: public:
CWLSurface() = default; CWLSurface() = default;
@ -17,13 +20,28 @@ class CWLSurface {
wlr_surface* wlr() const; wlr_surface* wlr() const;
bool exists() const; bool exists() const;
bool small() const; // means surface is smaller than the requested size
Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces
Vector2D getViewporterCorrectedSize() const;
CWLSurface& operator=(wlr_surface* pSurface) { // allow stretching. Useful for plugins.
destroy(); bool m_bFillIgnoreSmall = false;
m_pWLRSurface = pSurface;
init();
return *this; // if present, means this is a base surface of a window. Cleaned on unassign()
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;
init();
return *this;
} }
bool operator==(const CWLSurface& other) const { bool operator==(const CWLSurface& other) const {
@ -38,6 +56,10 @@ class CWLSurface {
return exists(); return exists();
} }
static CWLSurface* surfaceFromWlr(wlr_surface* pSurface) {
return (CWLSurface*)pSurface->data;
}
private: private:
wlr_surface* m_pWLRSurface = nullptr; wlr_surface* m_pWLRSurface = nullptr;

59
src/helpers/Watchdog.cpp Normal file
View file

@ -0,0 +1,59 @@
#include "Watchdog.hpp"
#include <signal.h>
#include "config/ConfigManager.hpp"
CWatchdog::~CWatchdog() {
m_bExitThread = true;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
m_pWatchdog.reset();
}
CWatchdog::CWatchdog() {
m_iMainThreadPID = pthread_self();
m_pWatchdog = std::make_unique<std::thread>([this] {
static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue;
while (1337) {
std::unique_lock lk(m_mWatchdogMutex);
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)
pthread_kill(m_iMainThreadPID, SIGUSR1);
}
if (m_bExitThread)
break;
m_bWatching = false;
m_bNotified = false;
}
});
m_pWatchdog->detach();
}
void CWatchdog::startWatching() {
static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue;
if (*PTIMEOUT == 0)
return;
m_tTriggered = std::chrono::high_resolution_clock::now();
m_bWillWatch = true;
m_bWatching = true;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
}
void CWatchdog::endWatching() {
m_bWatching = false;
m_bWillWatch = false;
m_bNotified = true;
m_cvWatchdogCondition.notify_all();
}

32
src/helpers/Watchdog.hpp Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <memory>
#include <chrono>
#include <thread>
#include <condition_variable>
class CWatchdog {
public:
// must be called from the main thread
CWatchdog();
~CWatchdog();
void startWatching();
void endWatching();
private:
std::chrono::high_resolution_clock::time_point m_tTriggered;
pthread_t m_iMainThreadPID = 0;
bool m_bWatching = false;
bool m_bWillWatch = false;
std::unique_ptr<std::thread> m_pWatchdog;
std::mutex m_mWatchdogMutex;
bool m_bNotified = false;
bool m_bExitThread = false;
std::condition_variable m_cvWatchdogCondition;
};
inline std::unique_ptr<CWatchdog> g_pWatchdog;

View file

@ -38,9 +38,10 @@ CWorkspace::~CWorkspace() {
} }
void CWorkspace::startAnim(bool in, bool left, bool instant) { void CWorkspace::startAnim(bool in, bool left, bool instant) {
const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle; const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle;
const auto PWORKSPACEGAP = &g_pConfigManager->getConfigValuePtr("general:gaps_workspaces")->intValue;
if (ANIMSTYLE.find("slidefade") == 0) { if (ANIMSTYLE.starts_with("slidefade")) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
float movePerc = 100.f; float movePerc = 100.f;
@ -54,7 +55,7 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
m_fAlpha.setValueAndWarp(1.f); m_fAlpha.setValueAndWarp(1.f);
m_vRenderOffset.setValueAndWarp(Vector2D(0, 0)); m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
if (ANIMSTYLE.find("slidefadevert") == 0) { if (ANIMSTYLE.starts_with("slidefadevert")) {
if (in) { if (in) {
m_fAlpha.setValueAndWarp(0.f); m_fAlpha.setValueAndWarp(0.f);
m_vRenderOffset.setValueAndWarp(Vector2D(0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f))); m_vRenderOffset.setValueAndWarp(Vector2D(0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f)));
@ -89,27 +90,29 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
} }
} else if (ANIMSTYLE == "slidevert") { } else if (ANIMSTYLE == "slidevert") {
// fallback is slide // fallback is slide
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto YDISTANCE = PMONITOR->vecSize.y + *PWORKSPACEGAP;
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
if (in) { if (in) {
m_vRenderOffset.setValueAndWarp(Vector2D(0, left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y)); m_vRenderOffset.setValueAndWarp(Vector2D(0, left ? YDISTANCE : -YDISTANCE));
m_vRenderOffset = Vector2D(0, 0); m_vRenderOffset = Vector2D(0, 0);
} else { } else {
m_vRenderOffset = Vector2D(0, left ? -PMONITOR->vecSize.y : PMONITOR->vecSize.y); m_vRenderOffset = Vector2D(0, left ? -YDISTANCE : YDISTANCE);
} }
} else { } else {
// fallback is slide // fallback is slide
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto XDISTANCE = PMONITOR->vecSize.x + *PWORKSPACEGAP;
m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. m_fAlpha.setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide.
if (in) { if (in) {
m_vRenderOffset.setValueAndWarp(Vector2D(left ? PMONITOR->vecSize.x : -PMONITOR->vecSize.x, 0)); m_vRenderOffset.setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0));
m_vRenderOffset = Vector2D(0, 0); m_vRenderOffset = Vector2D(0, 0);
} else { } else {
m_vRenderOffset = Vector2D(left ? -PMONITOR->vecSize.x : PMONITOR->vecSize.x, 0); m_vRenderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0);
} }
} }

View file

@ -4,9 +4,9 @@
#include <string> #include <string>
#include "../defines.hpp" #include "../defines.hpp"
enum eFullscreenMode : uint8_t enum eFullscreenMode : int8_t {
{ FULLSCREEN_INVALID = -1,
FULLSCREEN_FULL = 0, FULLSCREEN_FULL = 0,
FULLSCREEN_MAXIMIZED FULLSCREEN_MAXIMIZED
}; };
@ -49,12 +49,12 @@ class CWorkspace {
bool m_bDefaultFloating = false; bool m_bDefaultFloating = false;
bool m_bDefaultPseudo = false; bool m_bDefaultPseudo = false;
// don't destroy in sanity check
bool m_bIndestructible = false;
// last monitor (used on reconnect) // last monitor (used on reconnect)
std::string m_szLastMonitor = ""; std::string m_szLastMonitor = "";
// Whether the user configured command for on-created-empty has been executed, if any
bool m_bOnCreatedEmptyExecuted = false;
void startAnim(bool in, bool left, bool instant = false); void startAnim(bool in, bool left, bool instant = false);
void setActive(bool on); void setActive(bool on);

View file

@ -28,8 +28,7 @@ typedef struct {
} xcb_size_hints_t; } xcb_size_hints_t;
typedef unsigned int xcb_window_t; typedef unsigned int xcb_window_t;
typedef enum xcb_stack_mode_t typedef enum xcb_stack_mode_t {
{
XCB_STACK_MODE_ABOVE = 0, XCB_STACK_MODE_ABOVE = 0,
XCB_STACK_MODE_BELOW = 1, XCB_STACK_MODE_BELOW = 1,
XCB_STACK_MODE_TOP_IF = 2, XCB_STACK_MODE_TOP_IF = 2,

View file

@ -5,7 +5,7 @@ CHyprError::CHyprError() {
m_fFadeOpacity.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_NONE); m_fFadeOpacity.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_NONE);
m_fFadeOpacity.registerVar(); m_fFadeOpacity.registerVar();
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) { g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
if (!m_bIsCreated) if (!m_bIsCreated)
return; return;
@ -13,7 +13,7 @@ CHyprError::CHyprError() {
m_bMonitorChanged = true; m_bMonitorChanged = true;
}); });
g_pHookSystem->hookDynamic("preRender", [&](void* self, std::any param) { g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any param) {
if (!m_bIsCreated) if (!m_bIsCreated)
return; return;
@ -154,7 +154,7 @@ void CHyprError::draw() {
const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor; const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor;
wlr_box monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; CBox monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
m_bDamageBox.x = (int)PMONITOR->vecPosition.x; m_bDamageBox.x = (int)PMONITOR->vecPosition.x;
m_bDamageBox.y = (int)PMONITOR->vecPosition.y; m_bDamageBox.y = (int)PMONITOR->vecPosition.y;

Some files were not shown because too many files have changed in this diff Show more