Merge branch 'hyprwm:main' into main

This commit is contained in:
s1syph0s 2024-10-17 21:05:12 +02:00 committed by GitHub
commit 01dc79d5d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
265 changed files with 8926 additions and 4295 deletions

View file

@ -2,12 +2,13 @@ name: Bug Report
description: Something is not working right description: Something is not working right
labels: ["bug"] labels: ["bug"]
body: body:
- type: markdown - type: checkboxes
attributes: attributes:
value: | label: Already reported ? *
## Before opening a new issue, please take a moment to search through the current open and closed issues to check if it already exists. description: Before opening a new bug report, please take a moment to search through the current open and closed issues to check if it already exists.
options:
--- - label: I have searched the existing open and closed issues.
required: true
- type: dropdown - type: dropdown
id: type id: type
@ -28,10 +29,10 @@ body:
attributes: attributes:
label: System Info and Version label: System Info and Version
description: | description: |
Paste the output of `hyprctl systeminfo -c` here (If you are on a Paste the output of `hyprctl systeminfo -c` here. If you can't
version that shows you help menu, omit the `-c` and attach config files launch Hyprland, paste the output of `Hyprland --systeminfo`.
to the issue). If you have configs outside of the main config shown If `Hyprland --systeminfo` errors out (added in 0.44.0), find
here, please attach. and paste the Hyprland version manually.
value: "<details> value: "<details>
<summary>System/Version info</summary> <summary>System/Version info</summary>

View file

@ -1,3 +1,5 @@
name: Nix (Build)
on: on:
workflow_call: workflow_call:
secrets: secrets:
@ -10,21 +12,17 @@ jobs:
matrix: matrix:
package: package:
- hyprland - hyprland
- hyprland-cross
- xdg-desktop-portal-hyprland - xdg-desktop-portal-hyprland
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Clone repository - uses: DeterminateSystems/nix-installer-action@main
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
submodules: recursive
- uses: cachix/install-nix-action@v27
- uses: DeterminateSystems/magic-nix-cache-action@main - uses: DeterminateSystems/magic-nix-cache-action@main
- uses: cachix/cachix-action@v15 - uses: cachix/cachix-action@v15
with: with:
name: hyprland name: hyprland
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix build '.?submodules=1#${{ matrix.package }}' -L --extra-substituters "https://hyprland.cachix.org" - run: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#${{ matrix.package }}' -L --extra-substituters "https://hyprland.cachix.org"

View file

@ -1,15 +1,14 @@
name: Nix name: Nix (CI)
on: [push, pull_request, workflow_dispatch] on: [push, pull_request, workflow_dispatch]
jobs: jobs:
update-inputs: update-inputs:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
uses: ./.github/workflows/nix-update-inputs.yml uses: ./.github/workflows/nix-update-inputs.yml
secrets: inherit secrets: inherit
build: build:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) && !contains(needs.*.result, 'failure') if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
needs: update-inputs
uses: ./.github/workflows/nix-build.yml uses: ./.github/workflows/nix-build.yml
secrets: inherit secrets: inherit

View file

@ -1,4 +1,4 @@
name: Nix name: Nix (Update Inputs)
on: on:
workflow_call: workflow_call:
@ -8,6 +8,7 @@ on:
jobs: jobs:
update: update:
if: github.repository == 'hyprwm/Hyprland'
name: inputs name: inputs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View file

@ -20,7 +20,6 @@ jobs:
run: | run: |
git fetch --unshallow || echo "failed unshallowing" git fetch --unshallow || echo "failed unshallowing"
bash -c scripts/generateVersion.sh 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

View file

@ -7,12 +7,12 @@ name: Mark stale issues and pull requests
on: on:
schedule: schedule:
- cron: '7 */4 * * *' - cron: "7 */4 * * *"
workflow_dispatch: workflow_dispatch:
jobs: jobs:
stale: stale:
if: github.repository == 'hyprwm/Hyprland'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
issues: write issues: write
@ -22,7 +22,7 @@ jobs:
- uses: actions/stale@v5 - uses: actions/stale@v5
with: with:
repo-token: ${{ secrets.STALEBOT_PAT }} repo-token: ${{ secrets.STALEBOT_PAT }}
stale-issue-label: 'stale' stale-issue-label: "stale"
stale-pr-label: 'stale' stale-pr-label: "stale"
operations-per-run: 40 operations-per-run: 40
days-before-close: -1 days-before-close: -1

6
.gitignore vendored
View file

@ -14,6 +14,7 @@ _deps
build/ build/
result* result*
/.pre-commit-config.yaml
/.vscode/ /.vscode/
/.idea/ /.idea/
.envrc .envrc
@ -37,3 +38,8 @@ gmon.out
PKGBUILD PKGBUILD
src/version.h src/version.h
hyprpm/Makefile
hyprctl/Makefile
**/.#*.*
**/#*.*#

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.27) cmake_minimum_required(VERSION 3.30)
# Get version # Get version
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW) file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
@ -25,8 +25,18 @@ message(STATUS "Gathering git info")
execute_process(COMMAND ./scripts/generateVersion.sh execute_process(COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# udis find_package(PkgConfig REQUIRED)
# Try to find canihavesomecoffee's udis86 using pkgconfig vmd/udis86 does not
# provide a .pc file and won't be detected this way
pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2)
# Fallback to subproject
if(NOT udis_dep_FOUND)
add_subdirectory("subprojects/udis86") add_subdirectory("subprojects/udis86")
include_directories("subprojects/udis86")
message(STATUS "udis86 dependency not found, falling back to subproject")
endif()
if(CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER) string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
@ -47,11 +57,11 @@ else()
set(BUILDTYPE_LOWER "release") set(BUILDTYPE_LOWER "release")
endif() endif()
find_package(PkgConfig REQUIRED)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_SERVER_DIR wayland-server pkgdatadir) pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
message(
STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
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 "Configuring Hyprland in Debug with CMake") message(STATUS "Configuring Hyprland in Debug with CMake")
@ -61,8 +71,11 @@ else()
message(STATUS "Configuring Hyprland in Release with CMake") message(STATUS "Configuring Hyprland in Release with CMake")
endif() endif()
include_directories(. "src/" "subprojects/udis86/" "protocols/") add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
set(CMAKE_CXX_STANDARD 23)
include_directories(. "src/" "protocols/")
set(CMAKE_CXX_STANDARD 26)
add_compile_options( add_compile_options(
-Wall -Wall
-Wextra -Wextra
@ -87,16 +100,19 @@ else()
endif() endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.1)
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.2)
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
pkg_check_modules( pkg_check_modules(
deps deps
REQUIRED REQUIRED
IMPORTED_TARGET IMPORTED_TARGET
aquamarine
xkbcommon xkbcommon
uuid uuid
wayland-server wayland-server>=1.22.90
wayland-client
wayland-cursor
wayland-protocols wayland-protocols
cairo cairo
pango pango
@ -105,15 +121,11 @@ pkg_check_modules(
xcursor xcursor
libdrm libdrm
libinput libinput
hwdata
libseat
libdisplay-info
libliftoff
libudev
gbm gbm
gio-2.0
hyprlang>=0.3.2 hyprlang>=0.3.2
hyprcursor>=0.1.7 hyprcursor>=0.1.7
hyprutils>=0.2.1) hyprutils>=0.2.3)
find_package(hyprwayland-scanner 0.3.10 REQUIRED) find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@ -195,14 +207,11 @@ else()
REQUIRED REQUIRED
IMPORTED_TARGET IMPORTED_TARGET
xcb xcb
xwayland
xcb-util
xcb-render xcb-render
xcb-xfixes xcb-xfixes
xcb-icccm xcb-icccm
xcb-composite xcb-composite
xcb-res xcb-res
xcb-ewmh
xcb-errors) xcb-errors)
target_link_libraries(Hyprland PkgConfig::xdeps) target_link_libraries(Hyprland PkgConfig::xdeps)
endif() endif()
@ -225,14 +234,19 @@ target_precompile_headers(Hyprland PRIVATE
message(STATUS "Setting link libraries") message(STATUS "Setting link libraries")
target_link_libraries(Hyprland rt PkgConfig::deps) target_link_libraries(Hyprland rt PkgConfig::aquamarine_dep PkgConfig::deps)
if(udis_dep_FOUND)
target_link_libraries(Hyprland PkgConfig::udis_dep)
else()
target_link_libraries(Hyprland libudis86)
endif()
# used by `make installheaders`, to ensure the headers are generated # used by `make installheaders`, to ensure the headers are generated
add_custom_target(generate-protocol-headers) add_custom_target(generate-protocol-headers)
function(protocolnew protoPath protoName external) function(protocolnew protoPath protoName external)
if(external) if(external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath}) set(path ${protoPath})
else() else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif() endif()
@ -251,22 +265,31 @@ function(protocolWayland)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums COMMAND
${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ hyprwayland-scanner --wayland-enums
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp) target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
target_sources(generate-protocol-headers target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp) PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
endfunction() endfunction()
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
libudis86 uuid)
protocolnew("subprojects/hyprland-protocols/protocols" pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
"hyprland-global-shortcuts-v1" true) if(hyprland_protocols_dep_FOUND)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
else()
set(HYPRLAND_PROTOCOLS "subprojects/hyprland-protocols")
message(STATUS "hyprland-protocols subproject set to ${HYPRLAND_PROTOCOLS}")
endif()
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-global-shortcuts-v1"
true)
protocolnew("unstable/text-input" "text-input-unstable-v1" false) protocolnew("unstable/text-input" "text-input-unstable-v1" false)
protocolnew("subprojects/hyprland-protocols/protocols" protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1"
"hyprland-toplevel-export-v1" true) true)
protocolnew("protocols" "wlr-screencopy-unstable-v1" true) protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
protocolnew("protocols" "wlr-gamma-control-unstable-v1" true) protocolnew("protocols" "wlr-gamma-control-unstable-v1" true)
protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true) protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
@ -277,10 +300,11 @@ protocolnew("protocols" "input-method-unstable-v2" true)
protocolnew("protocols" "wlr-output-management-unstable-v1" true) protocolnew("protocols" "wlr-output-management-unstable-v1" true)
protocolnew("protocols" "kde-server-decoration" true) protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true) protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true) protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolnew("protocols" "wayland-drm" true) protocolnew("protocols" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("staging/tearing-control" "tearing-control-v1" false) protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false) protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false) protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false)
@ -309,6 +333,9 @@ protocolnew("stable/viewporter" "viewporter" false)
protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false) protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
protocolnew("staging/drm-lease" "drm-lease-v1" false) protocolnew("staging/drm-lease" "drm-lease-v1" false)
protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false) protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)
protocolwayland() protocolwayland()
@ -323,19 +350,21 @@ install(
CODE "execute_process( \ CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \ COMMAND ${CMAKE_COMMAND} -E create_symlink \
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \ ${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
${CMAKE_INSTALL_FULL_BINDIR}/hyprland \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
)") )")
# session file # session file
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions) DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
# allow Hyprland to find wallpapers # allow Hyprland to find assets
add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}") add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
# wallpapers # installable assets
file(GLOB_RECURSE WALLPAPERS "assets/wall*") file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr) list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
install(FILES ${INSTALLABLE_ASSETS}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
# default config # default config
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf

View file

@ -1,28 +1,24 @@
PREFIX = /usr/local PREFIX = /usr/local
legacyrenderer: legacyrenderer:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
cmake --build ./build --config Release --target all 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 -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
cmake --build ./build --config Release --target all 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 -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
cmake --build ./build --config Release --target all 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 -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
cmake --build ./build --config Debug --target all cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
chmod -R 777 ./build
nopch: nopch:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON -S . -B ./build -G Ninja cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON -S . -B ./build
cmake --build ./build --config Release --target all cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
clear: clear:
rm -rf build rm -rf build
@ -87,8 +83,8 @@ asan:
#git reset --hard #git reset --hard
@echo -en "If you want to apply a patch, input its path (leave empty for none):\n" @echo -en "If you want to apply a patch, input its path (leave empty for none):\n"
@read patchvar @read patchvar; \
@if [-n "$patchvar"]; then patch -p1 < $patchvar || echo ""; else echo "No patch specified"; fi if [ -n "$$patchvar" ]; then patch -p1 < "$$patchvar" || echo ""; else echo "No patch specified"; fi
git clone --recursive https://gitlab.freedesktop.org/wayland/wayland git clone --recursive https://gitlab.freedesktop.org/wayland/wayland
cd wayland && patch -p1 < ../scripts/waylandStatic.diff && meson setup build --buildtype=debug -Db_sanitize=address -Ddocumentation=false && ninja -C build && cd .. cd wayland && patch -p1 < ../scripts/waylandStatic.diff && meson setup build --buildtype=debug -Db_sanitize=address -Ddocumentation=false && ninja -C build && cd ..

View file

@ -1,6 +1,6 @@
<div align = center> <div align = center>
<img src="https://raw.githubusercontent.com/vaxerski/Hyprland/main/assets/header.svg" width="750" height="300" alt="banner"> <img src="https://raw.githubusercontent.com/hyprwm/Hyprland/main/assets/header.svg" width="750" height="300" alt="banner">
<br> <br>
@ -125,7 +125,6 @@ easy IPC, much more QoL stuff than other compositors and more...
<!----------------------------------{ Images }---------------------------------> <!----------------------------------{ Images }--------------------------------->
[Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg
[Preview A]: https://i.ibb.co/C1yTb0r/falf.png [Preview A]: https://i.ibb.co/C1yTb0r/falf.png
[Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.png [Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.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 +1 @@
0.41.2 0.44.0

BIN
assets/install/lockdead.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -0,0 +1,10 @@
globber = run_command('sh', '-c', 'find . -type f -not -name "*.build"', check: true)
files = globber.stdout().strip().split('\n')
foreach file : files
install_data(
file,
install_dir: join_paths(get_option('datadir'), 'hypr'),
install_tag: 'runtime',
)
endforeach

View file

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

View file

Before

Width:  |  Height:  |  Size: 5.9 MiB

After

Width:  |  Height:  |  Size: 5.9 MiB

View file

Before

Width:  |  Height:  |  Size: 27 MiB

After

Width:  |  Height:  |  Size: 27 MiB

View file

@ -1,7 +1,7 @@
wallpapers = ['0', '1', '2'] install_data(
'hyprland-portals.conf',
install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
install_tag: 'runtime',
)
foreach type : wallpapers subdir('install')
install_data(f'wall@type@.png', install_dir: join_paths(get_option('datadir'), 'hypr'), install_tag: 'runtime')
endforeach
install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime')

View file

@ -10,8 +10,8 @@ Hyprland - Dynamic tiling Wayland compositor
\f[B]Hyprland\f[R] [\f[I]arg [...]\f[R]]. \f[B]Hyprland\f[R] [\f[I]arg [...]\f[R]].
.SH DESCRIPTION .SH DESCRIPTION
.PP .PP
\f[B]Hyprland\f[R] is a dynamic tiling Wayland compositor based on \f[B]Hyprland\f[R] is an independent, highly customizable, dynamic
wlroots that doesn\[aq]t sacrifice on its looks. tiling Wayland compositor that doesn\[aq]t sacrifice on its looks.
.PP .PP
You can launch Hyprland by either going into a TTY and executing You can launch Hyprland by either going into a TTY and executing
\f[B]Hyprland\f[R], or with a login manager. \f[B]Hyprland\f[R], or with a login manager.

View file

@ -14,8 +14,8 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
=========== ===========
**Hyprland** is a dynamic tiling Wayland compositor based on **Hyprland** is an independent, highly customizable,
wlroots that doesn't sacrifice on its looks. dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
You can launch Hyprland by either going into a TTY and You can launch Hyprland by either going into a TTY and
executing **Hyprland**, or with a login manager. executing **Hyprland**, or with a login manager.

View file

@ -0,0 +1,14 @@
[Unit]
Description=Hyprland - Tiling compositor with the looks
Documentation=man:Hyprland(1)
BindsTo=graphical-session.target
Before=graphical-session.target
Wants=graphical-session-pre.target
After=graphical-session-pre.target
[Service]
Type=notify
ExecStart=/usr/bin/Hyprland
ExecStop=/usr/bin/hyprctl dispatch exit
Restart=on-failure
Slice=session.slice

View file

@ -0,0 +1,5 @@
[Desktop Entry]
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
Exec=systemctl --user start --wait hyprland-session
Type=Application

View file

@ -117,6 +117,13 @@ animations {
animation = workspaces, 1, 6, default animation = workspaces, 1, 6, default
} }
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all three if you wish to use that.
# workspace = w[t1], gapsout:0, gapsin:0, border: 0, rounding:0
# workspace = w[tg1], gapsout:0, gapsin:0, border: 0, rounding:0
# workspace = f[1], gapsout:0, gapsin:0, border: 0, rounding:0
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle { dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@ -169,9 +176,9 @@ device {
} }
#################### ###################
### KEYBINDINGSS ### ### KEYBINDINGS ###
#################### ###################
# See https://wiki.hyprland.org/Configuring/Keywords/ # See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier $mainMod = SUPER # Sets "Windows" key as main modifier
@ -228,6 +235,19 @@ bind = $mainMod, mouse_up, workspace, e-1
bindm = $mainMod, mouse:272, movewindow bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPause, exec, playerctl play-pause
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioPrev, exec, playerctl previous
############################## ##############################
### WINDOWS AND WORKSPACES ### ### WINDOWS AND WORKSPACES ###
@ -242,4 +262,8 @@ bindm = $mainMod, mouse:273, resizewindow
# Example windowrule v2 # Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this. # Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0

View file

@ -3,3 +3,5 @@ Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor Comment=An intelligent dynamic tiling Wayland compositor
Exec=Hyprland Exec=Hyprland
Type=Application Type=Application
DesktopNames=Hyprland
Keywords=tiling;wayland;compositor;

View file

@ -1,2 +1,10 @@
install_data('hyprland.conf', install_dir: join_paths(get_option('datadir'), 'hypr'), install_tag: 'runtime') install_data(
install_data('hyprland.desktop', install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), install_tag: 'runtime') 'hyprland.conf',
install_dir: join_paths(get_option('datadir'), 'hypr'),
install_tag: 'runtime',
)
install_data(
'hyprland.desktop',
install_dir: join_paths(get_option('datadir'), 'wayland-sessions'),
install_tag: 'runtime',
)

View file

@ -16,11 +16,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1722347739, "lastModified": 1728902391,
"narHash": "sha256-rAoh+K6KG+b1DwSWtqRVocdojnH6nGk6q07mNltoUSM=", "narHash": "sha256-44bnoY0nAvbBQ/lVjmn511yL39Sv7SknV0BDxn34P3Q=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "aquamarine", "repo": "aquamarine",
"rev": "7c3565f9bedc7cb601cc0baa14792247e4dc1d5a", "rev": "9874e08eec85b5542ca22494e127b0cdce46b786",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -29,6 +29,43 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"hyprcursor": { "hyprcursor": {
"inputs": { "inputs": {
"hyprlang": [ "hyprlang": [
@ -42,11 +79,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1721330371, "lastModified": 1728669738,
"narHash": "sha256-aYlHTWylczLt6ERJyg6E66Y/XSCbVL7leVcRuJmVbpI=", "narHash": "sha256-EDNAU9AYcx8OupUzbTbWE1d3HYdeG0wO6Msg3iL1muk=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprcursor", "repo": "hyprcursor",
"rev": "4493a972b48f9c3014befbbf381ed5fff91a65dc", "rev": "0264e698149fcb857a66a53018157b41f8d97bb0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -58,20 +95,18 @@
"hyprland-protocols": { "hyprland-protocols": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"xdph",
"nixpkgs" "nixpkgs"
], ],
"systems": [ "systems": [
"xdph",
"systems" "systems"
] ]
}, },
"locked": { "locked": {
"lastModified": 1721326555, "lastModified": 1728345020,
"narHash": "sha256-zCu4R0CSHEactW9JqYki26gy8h9f6rHmSwj4XJmlHgg=", "narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprland-protocols", "repo": "hyprland-protocols",
"rev": "5a11232266bf1a1f5952d5b179c3f4b2facaaa84", "rev": "a7c183800e74f337753de186522b9017a07a8cee",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -93,11 +128,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1721324361, "lastModified": 1728168612,
"narHash": "sha256-BiJKO0IIdnSwHQBSrEJlKlFr753urkLE48wtt0UhNG4=", "narHash": "sha256-AnB1KfiXINmuiW7BALYrKqcjCnsLZPifhb/7BsfPbns=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprlang", "repo": "hyprlang",
"rev": "adbefbf49664a6c2c8bf36b6487fd31e3eb68086", "rev": "f054f2e44d6a0b74607a6bc0f52dba337a3db38e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -116,11 +151,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1722098849, "lastModified": 1728941256,
"narHash": "sha256-D3wIZlBNh7LuZ0NaoCpY/Pvu+xHxIVtSN+KkWZYvvVs=", "narHash": "sha256-WRypmcZ2Bw94lLmcmxYokVOHPJSZ7T06V49QZ4tkZeQ=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprutils", "repo": "hyprutils",
"rev": "5dcbbc1e3de40b2cecfd2007434d86e924468f1f", "rev": "fd4be8b9ca932f7384e454bcd923c5451ef2aa85",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -139,11 +174,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1721324119, "lastModified": 1726874836,
"narHash": "sha256-SOOqIT27/X792+vsLSeFdrNTF+OSRp5qXv6Te+fb2Qg=", "narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprwayland-scanner", "repo": "hyprwayland-scanner",
"rev": "a048a6cb015340bd82f97c1f40a4b595ca85cc30", "rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -154,11 +189,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1722185531, "lastModified": 1728888510,
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=", "narHash": "sha256-nsNdSldaAyu6PE3YUA+YQLqUDJh+gRbBooMMekZJwvI=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d", "rev": "a3c0b3b21515f74fd2665903d4ce6bc4dc81c77c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -168,14 +203,55 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-stable": {
"locked": {
"lastModified": 1720386169,
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1728778939,
"narHash": "sha256-WybK5E3hpGxtCYtBwpRj1E9JoiVxe+8kX83snTNaFHE=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "ff68f91754be6f3427e4986d7949e6273659be1d",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"aquamarine": "aquamarine", "aquamarine": "aquamarine",
"hyprcursor": "hyprcursor", "hyprcursor": "hyprcursor",
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang", "hyprlang": "hyprlang",
"hyprutils": "hyprutils", "hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner", "hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems", "systems": "systems",
"xdph": "xdph" "xdph": "xdph"
} }
@ -197,10 +273,18 @@
}, },
"xdph": { "xdph": {
"inputs": { "inputs": {
"hyprland-protocols": "hyprland-protocols", "hyprland-protocols": [
"hyprland-protocols"
],
"hyprlang": [ "hyprlang": [
"hyprlang" "hyprlang"
], ],
"hyprutils": [
"hyprutils"
],
"hyprwayland-scanner": [
"hyprwayland-scanner"
],
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
@ -209,11 +293,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1722365976, "lastModified": 1728166987,
"narHash": "sha256-Khdm+mDzYA//XaU0M+hftod+rKr5q9SSHSEuiQ0/9ow=", "narHash": "sha256-w6dVTguAn9zJ+7aPOhBQgDz8bn6YZ7b56cY8Kg5HJRI=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland", "repo": "xdg-desktop-portal-hyprland",
"rev": "7f2a77ddf60390248e2a3de2261d7102a13e5341", "rev": "fb9c8d665af0588bb087f97d0f673ddf0d501787",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -22,6 +22,12 @@
inputs.hyprlang.follows = "hyprlang"; inputs.hyprlang.follows = "hyprlang";
}; };
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprlang = { hyprlang = {
url = "github:hyprwm/hyprlang"; url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -45,7 +51,15 @@
url = "github:hyprwm/xdg-desktop-portal-hyprland"; url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems"; inputs.systems.follows = "systems";
inputs.hyprland-protocols.follows = "hyprland-protocols";
inputs.hyprlang.follows = "hyprlang"; inputs.hyprlang.follows = "hyprlang";
inputs.hyprutils.follows = "hyprutils";
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
};
pre-commit-hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
@ -65,6 +79,15 @@
hyprland-extras hyprland-extras
]; ];
}); });
pkgsCrossFor = eachSystem (system: crossSystem:
import nixpkgs {
localSystem = system;
inherit crossSystem;
overlays = with self.overlays; [
hyprland-packages
hyprland-extras
];
});
in { in {
overlays = import ./nix/overlays.nix {inherit self lib inputs;}; overlays = import ./nix/overlays.nix {inherit self lib inputs;};
@ -74,6 +97,18 @@
self.packages.${system}) self.packages.${system})
// { // {
inherit (self.packages.${system}) xdg-desktop-portal-hyprland; inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
hyprland-treewide-formatter = {
enable = true;
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
pass_filenames = false;
excludes = ["subprojects"];
always_run = true;
};
};
};
}); });
packages = eachSystem (system: { packages = eachSystem (system: {
@ -90,25 +125,23 @@
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
; ;
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
}); });
devShells = eachSystem (system: { devShells = eachSystem (system: {
default = default =
pkgsFor.${system}.mkShell.override { pkgsFor.${system}.mkShell.override {
stdenv = pkgsFor.${system}.gcc13Stdenv; inherit (self.packages.${system}.default) stdenv;
} { } {
name = "hyprland-shell"; name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [
expat
libxml2
];
hardeningDisable = ["fortify"]; hardeningDisable = ["fortify"];
inputsFrom = [pkgsFor.${system}.hyprland]; inputsFrom = [pkgsFor.${system}.hyprland];
packages = [pkgsFor.${system}.clang-tools]; packages = [pkgsFor.${system}.clang-tools];
inherit (self.checks.${system}.pre-commit-check) shellHook;
}; };
}); });
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
nixosModules.default = import ./nix/module.nix inputs; nixosModules.default = import ./nix/module.nix inputs;
homeManagerModules.default = import ./nix/hm-module.nix self; homeManagerModules.default = import ./nix/hm-module.nix self;

View file

@ -9,7 +9,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.1.1)
add_executable(hyprctl "main.cpp") add_executable(hyprctl "main.cpp")
target_link_libraries(hyprctl PUBLIC PkgConfig::deps) target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
# binary # binary
install(TARGETS hyprctl) install(TARGETS hyprctl)

View file

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

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <string_view>
const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help] const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help]
commands: commands:

View file

@ -1,19 +1,19 @@
_hyprctl_cmd_2 () { _hyprctl_cmd_1 () {
hyprctl monitors | awk '/Monitor/{ print $2 }' hyprctl monitors | awk '/Monitor/{ print $2 }'
} }
_hyprctl_cmd_3 () { _hyprctl_cmd_3 () {
hyprpm list | awk '/Plugin/{ print $4 }'
}
_hyprctl_cmd_0 () {
hyprctl clients | awk '/class/{print $2}' hyprctl clients | awk '/class/{print $2}'
} }
_hyprctl_cmd_1 () { _hyprctl_cmd_2 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
} }
_hyprctl_cmd_0 () {
hyprpm list | awk '/Plugin/{print $4}'
}
_hyprctl () { _hyprctl () {
if [[ $(type -t _get_comp_words_by_ref) != function ]]; then if [[ $(type -t _get_comp_words_by_ref) != function ]]; then
echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed
@ -23,25 +23,25 @@ _hyprctl () {
local words cword local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword _get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize)
declare -A literal_transitions declare -A literal_transitions
literal_transitions[0]="([105]=1 [75]=2 [33]=3 [35]=4 [1]=2 [2]=2 [78]=2 [107]=5 [37]=2 [111]=4 [41]=2 [46]=2 [115]=2 [85]=6 [116]=8 [52]=2 [88]=4 [54]=2 [90]=9 [120]=2 [122]=2 [124]=2 [15]=2 [59]=10 [60]=2 [17]=11 [125]=12 [19]=2 [127]=2 [129]=2 [25]=13 [68]=2 [98]=4 [99]=2 [27]=2 [28]=14 [102]=2 [104]=4)" literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)"
literal_transitions[3]="([73]=17 [13]=2 [32]=17 [55]=17 [56]=17 [91]=17 [106]=2 [123]=2 [77]=1 [16]=2 [126]=17 [3]=1 [5]=2 [64]=17 [131]=2 [133]=17 [81]=17 [134]=17 [84]=17 [31]=17 [49]=2 [12]=2 [86]=17 [10]=17 [87]=17 [141]=17)" literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)"
literal_transitions[7]="([105]=1 [75]=2 [33]=3 [1]=2 [2]=2 [78]=2 [107]=5 [37]=2 [41]=2 [46]=2 [115]=2 [85]=6 [116]=8 [52]=2 [54]=2 [90]=9 [120]=2 [122]=2 [124]=2 [15]=2 [59]=10 [60]=2 [17]=11 [125]=12 [19]=2 [127]=2 [129]=2 [25]=13 [68]=2 [99]=2 [27]=2 [28]=14 [102]=2)" literal_transitions[3]="([139]=2 [63]=16 [64]=16 [45]=16 [105]=16 [27]=2 [26]=2 [52]=4 [5]=16 [66]=2 [67]=16 [129]=16 [113]=16 [12]=2 [74]=4 [99]=2 [35]=16 [152]=16 [98]=16 [59]=16 [117]=16 [41]=16 [17]=2 [138]=16 [154]=2 [122]=16)"
literal_transitions[8]="([101]=2 [130]=2 [132]=2 [0]=2 [74]=2 [36]=2 [108]=2 [109]=2 [38]=2 [110]=2 [4]=2 [79]=2 [40]=2 [80]=2 [113]=2 [6]=2 [42]=2 [43]=2 [82]=2 [83]=2 [47]=2 [48]=2 [9]=2 [50]=2 [51]=2 [53]=2 [11]=2 [112]=2 [89]=2 [118]=2 [57]=2 [92]=2 [58]=2 [93]=2 [94]=2 [61]=2 [62]=2 [128]=2 [95]=2 [63]=2 [20]=2 [97]=2 [22]=2 [23]=2 [65]=2 [66]=2 [135]=2 [136]=2 [24]=2 [26]=2 [69]=2 [100]=2 [70]=2 [140]=2 [29]=2 [71]=2)" literal_transitions[6]="([126]=2)"
literal_transitions[9]="([117]=20 [114]=16)" literal_transitions[10]="([56]=2)"
literal_transitions[11]="([103]=2)" literal_transitions[11]="([9]=2)"
literal_transitions[13]="([21]=1 [119]=1 [30]=1 [139]=1 [121]=1 [44]=1 [72]=1)" literal_transitions[12]="([14]=19 [80]=22)"
literal_transitions[14]="([39]=2)" literal_transitions[13]="([142]=2)"
literal_transitions[15]="([138]=2 [96]=2)" literal_transitions[14]="([0]=2 [84]=2 [2]=2 [85]=2 [4]=2 [87]=2 [88]=2 [90]=2 [91]=2 [92]=2 [93]=2 [94]=2 [96]=2 [15]=2 [18]=2 [103]=2 [21]=2 [104]=2 [23]=2 [24]=2 [28]=2 [29]=2 [30]=2 [108]=2 [111]=2 [32]=2 [112]=2 [36]=2 [38]=2 [119]=2 [124]=2 [46]=2 [47]=2 [48]=2 [49]=2 [53]=2 [55]=2 [131]=2 [132]=2 [134]=2 [135]=2 [60]=2 [136]=20 [141]=2 [65]=2 [144]=2 [145]=2 [68]=2 [147]=2 [70]=2 [71]=2 [72]=2 [73]=2 [148]=2 [75]=2 [76]=2 [150]=2 [153]=2)"
literal_transitions[17]="([18]=2 [7]=2)" literal_transitions[15]="([86]=4 [6]=4 [109]=4 [61]=4 [77]=4 [54]=4 [62]=4)"
literal_transitions[18]="([76]=19)" literal_transitions[16]="([40]=2 [44]=2)"
literal_transitions[19]="([34]=4 [45]=4)" literal_transitions[17]="([7]=23)"
literal_transitions[20]="([8]=2 [67]=2 [14]=2 [137]=2)" literal_transitions[18]="([31]=2 [149]=2)"
literal_transitions[19]="([95]=2 [16]=2 [115]=2 [20]=2)"
declare -A match_anything_transitions literal_transitions[20]="([106]=2 [82]=2 [127]=2 [1]=2 [83]=2)"
match_anything_transitions=([1]=2 [0]=7 [6]=2 [15]=2 [10]=2 [5]=15 [14]=18 [7]=7 [2]=18 [16]=2 [12]=2 [11]=18) literal_transitions[23]="([57]=21 [110]=21)"
declare -A match_anything_transitions=([6]=17 [7]=2 [0]=1 [22]=2 [5]=18 [4]=2 [2]=17 [18]=2 [11]=17 [8]=2 [9]=2 [13]=17 [10]=17 [1]=1)
declare -A subword_transitions declare -A subword_transitions
local state=0 local state=0
@ -79,21 +79,9 @@ _hyprctl () {
done done
local -a matches=()
local prefix="${words[$cword]}" local prefix="${words[$cword]}"
local shortest_suffix="$word"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate="${word##*$char}"
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$word" ]]; then
local superfluous_prefix=${word%$shortest_suffix}
fi
if [[ -v "literal_transitions[$state]" ]]; then if [[ -v "literal_transitions[$state]" ]]; then
local state_transitions_initializer=${literal_transitions[$state]} local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions declare -A state_transitions
@ -102,25 +90,38 @@ _hyprctl () {
for literal_id in "${!state_transitions[@]}"; do for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}" local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"} matches+=("$literal ")
COMPREPLY+=("$completion ")
fi fi
done done
fi fi
declare -A commands declare -A commands
commands=([5]=1 [16]=2 [12]=3 [10]=0) commands=([7]=0 [22]=1 [8]=3 [5]=2)
if [[ -v "commands[$state]" ]]; then if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]} local command_id=${commands[$state]}
local completions=() local completions=()
mapfile -t completions < <(_hyprctl_cmd_${command_id} "$prefix" | cut -f1) readarray -t completions < <(_hyprctl_cmd_${command_id} "$prefix" | cut -f1)
for item in "${completions[@]}"; do for item in "${completions[@]}"; do
if [[ $item = "${prefix}"* ]]; then if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item") matches+=("$item")
fi fi
done done
fi fi
local shortest_suffix="$prefix"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate=${prefix##*$char}
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$prefix" ]]; then
local superfluous_prefix=${prefix%$shortest_suffix}
fi
COMPREPLY=("${matches[@]#$superfluous_prefix}")
return 0 return 0
} }

View file

@ -1,21 +1,21 @@
function _hyprctl_3 function _hyprctl_2
set 1 $argv[1] set 1 $argv[1]
hyprctl monitors | awk '/Monitor/{ print $2 }' hyprctl monitors | awk '/Monitor/{ print $2 }'
end end
function _hyprctl_4 function _hyprctl_4
set 1 $argv[1] set 1 $argv[1]
hyprpm list | awk '/Plugin/{ print $4 }' hyprctl clients | awk '/class/{print $2}'
end
function _hyprctl_3
set 1 $argv[1]
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
end end
function _hyprctl_1 function _hyprctl_1
set 1 $argv[1] set 1 $argv[1]
hyprctl clients | awk '/class/{ print $2 }' hyprpm list | awk '/Plugin/{print $4}'
end
function _hyprctl_2
set 1 $argv[1]
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
end end
function _hyprctl function _hyprctl
@ -29,145 +29,160 @@ function _hyprctl
set COMP_CWORD (count $COMP_WORDS) set COMP_CWORD (count $COMP_WORDS)
end end
set --local literals "cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow" set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize"
set --local descriptions set descriptions
set descriptions[1] "Focus the next window on a workspace" set descriptions[1] "Resize the active window"
set descriptions[3] "Get the current cursor pos in global layout coordinates" set descriptions[2] "Fullscreen"
set descriptions[5] "Rename a workspace" set descriptions[3] "Switch to the next window in a group"
set descriptions[7] "Focus the first window matching" set descriptions[4] "Refresh state after issuing the command"
set descriptions[10] "Swap the focused window with the next window" set descriptions[5] "Move the active window into a group"
set descriptions[12] "Move the active window" set descriptions[7] "CONFUSED"
set descriptions[16] "List the layers" set descriptions[9] "Print system info"
set descriptions[18] "List active outputs with their properties" set descriptions[11] "List all layouts available (including plugin ones)"
set descriptions[20] "Get into a kill mode, where you can kill an app by clicking on it" set descriptions[12] "Set a property of a window"
set descriptions[21] "Set the current window's floating state to false" set descriptions[14] "Set the xkb layout index for a keyboard"
set descriptions[22] "ERROR" set descriptions[16] "Prohibit the active window from becoming or being inserted into group"
set descriptions[23] "Focus a monitor" set descriptions[19] "Execute a shell command"
set descriptions[24] "Swap the active window with another window" set descriptions[20] "Set the cursor theme and reloads the cursor manager"
set descriptions[25] "Move the active window out of a group" set descriptions[22] "Focus the urgent window or the last window"
set descriptions[26] "Send a notification using the built-in Hyprland notification system" set descriptions[23] "Get the list of defined workspace rules"
set descriptions[27] "Move the cursor to a specified position" set descriptions[24] "Move the active workspace to a monitor"
set descriptions[28] "Set the cursor theme and reloads the cursor manager" set descriptions[25] "Move window doesnt switch to the workspace"
set descriptions[29] "Set the hyprctl error string" set descriptions[26] "Interact with hyprpaper if present"
set descriptions[30] "Move the active workspace to a monitor" set descriptions[29] "Swap the active window with the next or previous in a group"
set descriptions[31] "CONFUSED" set descriptions[30] "Move the cursor to the corner of the active window"
set descriptions[34] "Set a property of a window" set descriptions[31] "Move a selected window"
set descriptions[35] "Specify the Hyprland instance" set descriptions[33] "Move the active window in a direction or to a monitor"
set descriptions[36] "Disable output" set descriptions[34] "Lists all global shortcuts"
set descriptions[37] "Toggle the current window's floating state" set descriptions[35] "List all windows with their properties"
set descriptions[38] "Get the list of defined workspace rules" set descriptions[37] "Temporarily enable or disable binds:ignore_group_lock"
set descriptions[39] "Move the focused window to a workspace" set descriptions[38] "Print the current random splash"
set descriptions[41] "Temporarily enable or disable binds:ignore_group_lock" set descriptions[39] "Execute a raw shell command"
set descriptions[42] "List all workspaces with their properties" set descriptions[40] "List active outputs with their properties"
set descriptions[43] "Swap the active window with the next or previous in a group" set descriptions[43] "Disable output"
set descriptions[44] "Close a specified window" set descriptions[44] "Gets the current config info about animations and beziers"
set descriptions[45] "WARNING" set descriptions[47] "Change the split ratio"
set descriptions[46] "Specify the Hyprland instance" set descriptions[48] "Move the active window"
set descriptions[47] "List all registered binds" set descriptions[49] "Pass the key to a specified window"
set descriptions[48] "Move the active window in a direction or to a monitor" set descriptions[50] "Swap the focused window with the next window"
set descriptions[49] "Change the split ratio" set descriptions[51] "List all connected keyboards and mice"
set descriptions[51] "Prohibit the active window from becoming or being inserted into group" set descriptions[52] "List the layers"
set descriptions[52] "Change the workspace" set descriptions[54] "Lock the focused group"
set descriptions[53] "List all current config parsing errors" set descriptions[55] "OK"
set descriptions[54] "Toggle the current active window into a group" set descriptions[56] "Move a workspace to a monitor"
set descriptions[55] "Get the config option status (values)" set descriptions[58] "Specify the Hyprland instance"
set descriptions[58] "Close the active window" set descriptions[59] "Disable output"
set descriptions[59] "Pass the key to a specified window" set descriptions[61] "Pin a window"
set descriptions[60] "List all decorations and their info" set descriptions[62] "WARNING"
set descriptions[61] "List all connected keyboards and mice" set descriptions[63] "INFO"
set descriptions[62] "Switch focus from current to previously focused window" set descriptions[66] "Set the current window's floating state to true"
set descriptions[63] "Change the current mapping group" set descriptions[69] "On shortcut X sends shortcut Y to a specified window"
set descriptions[64] "Execute a Global Shortcut using the GlobalShortcuts portal" set descriptions[70] "List all workspaces with their properties"
set descriptions[66] "Force the renderer to reload all resources and outputs" set descriptions[71] "Focus the next window on a workspace"
set descriptions[67] "Move a selected window" set descriptions[72] "Modify the window stack order of the active or specified window"
set descriptions[69] "Print the Hyprland version: flags, commit and branch of build" set descriptions[73] "Toggle the current active window into a group"
set descriptions[70] "Set all monitors' DPMS status" set descriptions[74] "Lock the groups"
set descriptions[71] "Resize the active window" set descriptions[76] "Set all monitors' DPMS status"
set descriptions[72] "Move the active window into a group" set descriptions[77] "Switch focus from current to previously focused window"
set descriptions[73] "OK" set descriptions[78] "No Icon"
set descriptions[75] "Set the current window's floating state to true" set descriptions[79] "Execute a batch of commands separated by ;"
set descriptions[76] "Print tail of the log" set descriptions[80] "Send a notification using the built-in Hyprland notification system"
set descriptions[79] "List all layouts available (including plugin ones)" set descriptions[82] "List all running Hyprland instances and their info"
set descriptions[80] "Move a workspace to a monitor" set descriptions[83] "Maximize no fullscreen"
set descriptions[81] "Execute a shell command" set descriptions[84] "Maximize and fullscreen"
set descriptions[83] "Modify the window stack order of the active or specified window" set descriptions[85] "Move the active window out of a group"
set descriptions[84] "Toggle the focused window's internal fullscreen state" set descriptions[86] "Close the active window"
set descriptions[86] "Issue a keyword to call a config keyword dynamically" set descriptions[87] "HINT"
set descriptions[89] "Disable output" set descriptions[88] "Move the focused window to a workspace"
set descriptions[90] "Pin a window" set descriptions[89] "Move the cursor to a specified position"
set descriptions[91] "Allows adding/removing fake outputs to a specific backend" set descriptions[90] "List all current config parsing errors"
set descriptions[93] "Toggle a special workspace on/off" set descriptions[91] "Close a specified window"
set descriptions[94] "Toggle the focused window's fullscreen state" set descriptions[92] "Swap the active window with another window"
set descriptions[95] "Toggle the current window to always be opaque" set descriptions[93] "Apply a tag to the window"
set descriptions[96] "Focus the requested workspace" set descriptions[94] "Force the renderer to reload all resources and outputs"
set descriptions[98] "Switch to the next window in a group" set descriptions[95] "Center the active window"
set descriptions[99] "Output in JSON format" set descriptions[97] "Focus the first window matching"
set descriptions[100] "List all running Hyprland instances and their info" set descriptions[98] "Set the hyprctl error string"
set descriptions[101] "Execute a raw shell command" set descriptions[101] "List all registered binds"
set descriptions[102] "Exit the compositor with no questions asked" set descriptions[102] "Print the Hyprland version: flags, commit and branch of build"
set descriptions[103] "List all windows with their properties" set descriptions[103] "Prints the help message"
set descriptions[105] "Execute a batch of commands separated by ;" set descriptions[104] "Toggle a special workspace on/off"
set descriptions[106] "Dismiss all or up to amount of notifications" set descriptions[105] "Toggle the focused window's fullscreen state"
set descriptions[108] "Set the xkb layout index for a keyboard" set descriptions[107] "None"
set descriptions[109] "Move window doesnt switch to the workspace" set descriptions[108] "Issue a keyword to call a config keyword dynamically"
set descriptions[110] "Apply a tag to the window" set descriptions[109] "Toggle the current window to always be opaque"
set descriptions[111] "Behave as moveintogroup" set descriptions[110] "ERROR"
set descriptions[112] "Refresh state after issuing the command" set descriptions[111] "Specify the Hyprland instance"
set descriptions[113] "Move the focus in a direction" set descriptions[112] "Toggle the current window's floating state"
set descriptions[114] "Focus the urgent window or the last window" set descriptions[113] "Rename a workspace"
set descriptions[116] "Get the active workspace name and its properties" set descriptions[115] "Get the active workspace name and its properties"
set descriptions[117] "Issue a dispatch to call a keybind dispatcher with an arg" set descriptions[117] "Get into a kill mode, where you can kill an app by clicking on it"
set descriptions[119] "Center the active window" set descriptions[119] "Allows adding/removing fake outputs to a specific backend"
set descriptions[120] "HINT" set descriptions[120] "Execute a Global Shortcut using the GlobalShortcuts portal"
set descriptions[121] "Interact with hyprpaper if present" set descriptions[121] "Issue a dispatch to call a keybind dispatcher with an arg"
set descriptions[122] "No Icon" set descriptions[122] "Force reload the config"
set descriptions[123] "Force reload the config" set descriptions[124] "Output in JSON format"
set descriptions[125] "Print system info" set descriptions[125] "Emits a custom event to socket2"
set descriptions[126] "Interact with a plugin" set descriptions[126] "Prints the help message"
set descriptions[128] "Get the active window name and its properties" set descriptions[128] "Current"
set descriptions[129] "Swap the active workspaces between two monitors" set descriptions[129] "Get the active window name and its properties"
set descriptions[130] "Print the current random splash" set descriptions[131] "Dismiss all or up to amount of notifications"
set descriptions[131] "On shortcut X sends shortcut Y to a specified window" set descriptions[132] "Focus a monitor"
set descriptions[133] "Lock the focused group" set descriptions[133] "Move the focus in a direction"
set descriptions[136] "Lock the groups" set descriptions[134] "Interact with a plugin"
set descriptions[137] "Move the cursor to the corner of the active window" set descriptions[135] "Exit the compositor with no questions asked"
set descriptions[140] "INFO" set descriptions[136] "Change the workspace"
set descriptions[141] "Resize a selected window" set descriptions[137] "Sets the focused windows fullscreen mode and the one sent to the client"
set descriptions[138] "Get the config option status (values)"
set descriptions[141] "List all decorations and their info"
set descriptions[142] "Set the current window's floating state to false"
set descriptions[144] "Return a parsable JSON with all the config options, descriptions, value types and ranges"
set descriptions[145] "Resize a selected window"
set descriptions[146] "Toggle the focused window's internal fullscreen state"
set descriptions[147] "Print tail of the log"
set descriptions[148] "Swap the active workspaces between two monitors"
set descriptions[149] "Change the current mapping group"
set descriptions[151] "Behave as moveintogroup"
set descriptions[152] "Get the current cursor pos in global layout coordinates"
set descriptions[154] "Focus the requested workspace"
set --local literal_transitions set literal_transitions
set literal_transitions[1] "set inputs 106 76 34 36 2 3 79 108 38 112 42 47 116 86 117 53 89 55 91 121 123 125 16 60 61 18 126 20 128 130 26 69 99 100 28 29 103 105; set tos 2 3 4 5 3 3 3 6 3 5 3 3 3 7 9 3 5 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 5 3 3 15 3 5" set literal_transitions[1] "set inputs 121 44 126 82 4 52 51 129 90 59 9 11 12 131 14 98 102 103 134 101 138 23 20 141 26 144 108 147 70 34 35 79 115 38 152 117 122 124 40 43 80 119; set tos 15 3 22 3 22 3 3 3 3 22 3 3 4 5 6 7 3 22 8 3 3 3 3 9 3 3 10 11 3 3 3 22 3 3 3 3 14 22 12 22 16 13"
set literal_transitions[4] "set inputs 74 14 33 56 57 92 107 124 78 17 127 4 6 65 132 134 82 135 85 32 50 13 87 11 88 142; set tos 18 3 18 18 18 18 3 3 2 3 18 2 3 18 3 18 18 18 18 18 3 3 18 18 18 18" set literal_transitions[2] "set inputs 82 52 51 129 9 90 11 12 131 14 98 102 134 101 23 20 138 141 26 144 108 147 70 34 35 115 38 152 117 40 119 122 121 80 44; set tos 3 3 3 3 3 3 3 4 5 6 7 3 8 3 3 3 3 9 3 3 10 11 3 3 3 3 3 3 3 12 13 14 15 16 3"
set literal_transitions[8] "set inputs 106 76 34 2 3 79 108 38 42 47 116 86 117 53 55 91 121 123 125 16 60 61 18 126 20 128 130 26 69 100 28 29 103; set tos 2 3 4 3 3 3 6 3 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 3 3 15 3" set literal_transitions[4] "set inputs 140 64 65 46 106 28 27 53 6 67 68 130 114 13 75 100 36 153 99 60 118 42 18 139 155 123; set tos 3 17 17 17 17 3 3 5 17 3 17 17 17 3 5 3 17 17 17 17 17 17 3 17 3 17"
set literal_transitions[9] "set inputs 102 131 133 1 75 37 109 110 39 111 5 80 41 81 114 7 43 44 83 84 48 49 10 51 52 54 12 113 90 119 58 93 59 94 95 62 63 129 96 64 21 98 23 24 66 67 136 137 25 27 70 101 71 141 30 72; set tos 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3" set literal_transitions[7] "set inputs 127; set tos 3"
set literal_transitions[10] "set inputs 118 115; set tos 21 17" set literal_transitions[11] "set inputs 57; set tos 3"
set literal_transitions[12] "set inputs 104; set tos 3" set literal_transitions[12] "set inputs 10; set tos 3"
set literal_transitions[14] "set inputs 22 120 31 140 122 45 73; set tos 2 2 2 2 2 2 2" set literal_transitions[13] "set inputs 15 81; set tos 20 23"
set literal_transitions[15] "set inputs 40; set tos 3" set literal_transitions[14] "set inputs 143; set tos 3"
set literal_transitions[16] "set inputs 139 97; set tos 3 3" set literal_transitions[15] "set inputs 1 85 3 86 5 88 89 91 92 93 94 95 97 16 19 104 22 105 24 25 29 30 31 109 112 33 113 37 39 120 125 47 48 49 50 54 56 132 133 135 136 61 137 142 66 145 146 69 148 71 72 73 74 149 76 77 151 154; set tos 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 21 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3"
set literal_transitions[18] "set inputs 19 8; set tos 3 3" set literal_transitions[16] "set inputs 87 7 110 62 78 55 63; set tos 5 5 5 5 5 5 5"
set literal_transitions[19] "set inputs 77; set tos 20" set literal_transitions[17] "set inputs 41 45; set tos 3 3"
set literal_transitions[20] "set inputs 35 46; set tos 5 5" set literal_transitions[18] "set inputs 8; set tos 24"
set literal_transitions[21] "set inputs 9 68 15 138; set tos 3 3 3 3" set literal_transitions[19] "set inputs 32 150; set tos 3 3"
set literal_transitions[20] "set inputs 96 17 116 21; set tos 3 3 3 3"
set literal_transitions[21] "set inputs 107 83 128 2 84; set tos 3 3 3 3 3"
set literal_transitions[24] "set inputs 58 111; set tos 22 22"
set --local match_anything_transitions_from 2 1 7 16 11 6 15 8 3 17 13 12 set match_anything_transitions_from 7 8 1 23 6 5 3 19 12 9 10 14 11 2
set --local match_anything_transitions_to 3 8 3 3 3 16 19 8 19 3 3 19 set match_anything_transitions_to 18 3 2 3 19 3 18 3 18 3 3 18 18 2
set --local state 1 set state 1
set --local word_index 2 set word_index 2
while test $word_index -lt $COMP_CWORD while test $word_index -lt $COMP_CWORD
set --local -- word $COMP_WORDS[$word_index] set -- word $COMP_WORDS[$word_index]
if set --query literal_transitions[$state] && test -n $literal_transitions[$state] if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
eval $literal_transitions[$state] eval $literal_transitions[$state]
if contains -- $word $literals if contains -- $word $literals
set --local literal_matched 0 set literal_matched 0
for literal_id in (seq 1 (count $literals)) for literal_id in (seq 1 (count $literals))
if test $literals[$literal_id] = $word if test $literals[$literal_id] = $word
set --local index (contains --index -- $literal_id $inputs) set index (contains --index -- $literal_id $inputs)
set state $tos[$index] set state $tos[$index]
set word_index (math $word_index + 1) set word_index (math $word_index + 1)
set literal_matched 1 set literal_matched 1
@ -181,7 +196,7 @@ function _hyprctl
end end
if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state] if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state]
set --local index (contains --index -- $state $match_anything_transitions_from) set index (contains --index -- $state $match_anything_transitions_from)
set state $match_anything_transitions_to[$index] set state $match_anything_transitions_to[$index]
set word_index (math $word_index + 1) set word_index (math $word_index + 1)
continue continue
@ -191,8 +206,8 @@ function _hyprctl
end end
if set --query literal_transitions[$state] && test -n $literal_transitions[$state] if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
eval $literal_transitions[$state] eval $literal_transitions[$state]
for literal_id in $inputs for literal_id in $inputs
if test -n $descriptions[$literal_id] if test -n $descriptions[$literal_id]
@ -203,14 +218,14 @@ function _hyprctl
end end
end end
set command_states 6 17 13 11 set command_states 8 23 9 6
set command_ids 2 3 4 1 set command_ids 1 2 4 3
if contains $state $command_states if contains $state $command_states
set --local index (contains --index $state $command_states) set index (contains --index $state $command_states)
set --local function_id $command_ids[$index] set function_id $command_ids[$index]
set --local function_name _hyprctl_$function_id set function_name _hyprctl_$function_id
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
$function_name "$COMP_WORDS[$COMP_CWORD]" $function_name "$COMP_WORDS[$COMP_CWORD]"
end end

View file

@ -9,6 +9,7 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (-r) "Refresh state after issuing the command" | (-r) "Refresh state after issuing the command"
| (--batch) "Execute a batch of commands separated by ;" | (--batch) "Execute a batch of commands separated by ;"
| (-q | --quiet) "Disable output" | (-q | --quiet) "Disable output"
| (-h | --help) "Prints the help message"
; ;
<WINDOWS> ::= {{{ hyprctl clients | awk '/class/{print $2}' }}}; <WINDOWS> ::= {{{ hyprctl clients | awk '/class/{print $2}' }}};
@ -59,16 +60,18 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
<ARGUMENTS> ::= (activewindow) "Get the active window name and its properties" <ARGUMENTS> ::= (activewindow) "Get the active window name and its properties"
| (activeworkspace) "Get the active workspace name and its properties" | (activeworkspace) "Get the active workspace name and its properties"
| (animations) "Gets the current config info about animations and beziers"
| (binds) "List all registered binds" | (binds) "List all registered binds"
| (clients) "List all windows with their properties" | (clients) "List all windows with their properties"
| (configerrors) "List all current config parsing errors" | (configerrors) "List all current config parsing errors"
| (cursorpos) "Get the current cursor pos in global layout coordinates" | (cursorpos) "Get the current cursor pos in global layout coordinates"
| (decorations <WINDOWS>) "List all decorations and their info" | (decorations <WINDOWS>) "List all decorations and their info"
| (descriptions) "Return a parsable JSON with all the config options, descriptions, value types and ranges"
| (devices) "List all connected keyboards and mice" | (devices) "List all connected keyboards and mice"
| (dismissnotify <NUM>) "Dismiss all or up to amount of notifications" | (dismissnotify <NUM>) "Dismiss all or up to amount of notifications"
| (dispatch <DISPATCHERS>) "Issue a dispatch to call a keybind dispatcher with an arg" | (dispatch <DISPATCHERS>) "Issue a dispatch to call a keybind dispatcher with an arg"
| (getoption) "Get the config option status (values)" | (getoption) "Get the config option status (values)"
| (globalshortcuts) "" | (globalshortcuts) "Lists all global shortcuts"
| (hyprpaper) "Interact with hyprpaper if present" | (hyprpaper) "Interact with hyprpaper if present"
| (instances) "List all running Hyprland instances and their info" | (instances) "List all running Hyprland instances and their info"
| (keyword <KEYWORDS>) "Issue a keyword to call a config keyword dynamically" | (keyword <KEYWORDS>) "Issue a keyword to call a config keyword dynamically"
@ -79,8 +82,8 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (notify <NOTIFICATION_TYPES> <NUM>) "Send a notification using the built-in Hyprland notification system" | (notify <NOTIFICATION_TYPES> <NUM>) "Send a notification using the built-in Hyprland notification system"
| (output (create (wayland | x11 | headless | auto) | remove <MONITORS>)) "Allows adding/removing fake outputs to a specific backend" | (output (create (wayland | x11 | headless | auto) | remove <MONITORS>)) "Allows adding/removing fake outputs to a specific backend"
| (plugin <AVAILABLE_PLUGINS>) "Interact with a plugin" | (plugin <AVAILABLE_PLUGINS>) "Interact with a plugin"
| (reload) "Force reload the config" | (reload [config-only]) "Force reload the config"
| (rollinglog) "Print tail of the log" | (rollinglog [-f]) "Print tail of the log"
| (setcursor) "Set the cursor theme and reloads the cursor manager" | (setcursor) "Set the cursor theme and reloads the cursor manager"
| (seterror [disable]) "Set the hyprctl error string" | (seterror [disable]) "Set the hyprctl error string"
| (setprop <PROPS>) "Set a property of a window" | (setprop <PROPS>) "Set a property of a window"
@ -92,6 +95,13 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (workspaces) "List all workspaces with their properties" | (workspaces) "List all workspaces with their properties"
; ;
<WINDOW_STATE> ::= (-1) "Current"
| (0) "None"
| (1) "Maximize no fullscreen"
| (2) "Fullscreen"
| (3) "Maximize and fullscreen"
;
<DISPATCHERS> ::= (exec) "Execute a shell command" <DISPATCHERS> ::= (exec) "Execute a shell command"
| (execr) "Execute a raw shell command" | (execr) "Execute a raw shell command"
| (pass) "Pass the key to a specified window" | (pass) "Pass the key to a specified window"
@ -106,6 +116,7 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (settiled) "Set the current window's floating state to false" | (settiled) "Set the current window's floating state to false"
| (fullscreen) "Toggle the focused window's fullscreen state" | (fullscreen) "Toggle the focused window's fullscreen state"
| (fakefullscreen) "Toggle the focused window's internal fullscreen state" | (fakefullscreen) "Toggle the focused window's internal fullscreen state"
| (fullscreenstate <WINDOW_STATE>) "Sets the focused windows fullscreen mode and the one sent to the client"
| (dpms) "Set all monitors' DPMS status" | (dpms) "Set all monitors' DPMS status"
| (pin) "Pin a window" | (pin) "Pin a window"
| (movefocus) "Move the focus in a direction" | (movefocus) "Move the focus in a direction"
@ -148,4 +159,5 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (setignoregrouplock) "Temporarily enable or disable binds:ignore_group_lock" | (setignoregrouplock) "Temporarily enable or disable binds:ignore_group_lock"
| (global) "Execute a Global Shortcut using the GlobalShortcuts portal" | (global) "Execute a Global Shortcut using the GlobalShortcuts portal"
| (submap) "Change the current mapping group" | (submap) "Change the current mapping group"
| (event) "Emits a custom event to socket2"
; ;

View file

@ -1,145 +1,160 @@
#compdef hyprctl #compdef hyprctl
_hyprctl_cmd_2 () { _hyprctl_cmd_1 () {
hyprctl monitors | awk '/Monitor/{ print $2 }' hyprctl monitors | awk '/Monitor/{ print $2 }'
} }
_hyprctl_cmd_3 () { _hyprctl_cmd_3 () {
hyprpm list | awk '/Plugin/{ print $4 }'
}
_hyprctl_cmd_0 () {
hyprctl clients | awk '/class/{print $2}' hyprctl clients | awk '/class/{print $2}'
} }
_hyprctl_cmd_1 () { _hyprctl_cmd_2 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
} }
_hyprctl_cmd_0 () {
hyprpm list | awk '/Plugin/{print $4}'
}
_hyprctl () { _hyprctl () {
local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize")
local -A descriptions local -A descriptions
descriptions[1]="Focus the next window on a workspace" descriptions[1]="Resize the active window"
descriptions[3]="Get the current cursor pos in global layout coordinates" descriptions[2]="Fullscreen"
descriptions[5]="Rename a workspace" descriptions[3]="Switch to the next window in a group"
descriptions[7]="Focus the first window matching" descriptions[4]="Refresh state after issuing the command"
descriptions[10]="Swap the focused window with the next window" descriptions[5]="Move the active window into a group"
descriptions[12]="Move the active window" descriptions[7]="CONFUSED"
descriptions[16]="List the layers" descriptions[9]="Print system info"
descriptions[18]="List active outputs with their properties" descriptions[11]="List all layouts available (including plugin ones)"
descriptions[20]="Get into a kill mode, where you can kill an app by clicking on it" descriptions[12]="Set a property of a window"
descriptions[21]="Set the current window's floating state to false" descriptions[14]="Set the xkb layout index for a keyboard"
descriptions[22]="ERROR" descriptions[16]="Prohibit the active window from becoming or being inserted into group"
descriptions[23]="Focus a monitor" descriptions[19]="Execute a shell command"
descriptions[24]="Swap the active window with another window" descriptions[20]="Set the cursor theme and reloads the cursor manager"
descriptions[25]="Move the active window out of a group" descriptions[22]="Focus the urgent window or the last window"
descriptions[26]="Send a notification using the built-in Hyprland notification system" descriptions[23]="Get the list of defined workspace rules"
descriptions[27]="Move the cursor to a specified position" descriptions[24]="Move the active workspace to a monitor"
descriptions[28]="Set the cursor theme and reloads the cursor manager" descriptions[25]="Move window doesnt switch to the workspace"
descriptions[29]="Set the hyprctl error string" descriptions[26]="Interact with hyprpaper if present"
descriptions[30]="Move the active workspace to a monitor" descriptions[29]="Swap the active window with the next or previous in a group"
descriptions[31]="CONFUSED" descriptions[30]="Move the cursor to the corner of the active window"
descriptions[34]="Set a property of a window" descriptions[31]="Move a selected window"
descriptions[35]="Specify the Hyprland instance" descriptions[33]="Move the active window in a direction or to a monitor"
descriptions[36]="Disable output" descriptions[34]="Lists all global shortcuts"
descriptions[37]="Toggle the current window's floating state" descriptions[35]="List all windows with their properties"
descriptions[38]="Get the list of defined workspace rules" descriptions[37]="Temporarily enable or disable binds:ignore_group_lock"
descriptions[39]="Move the focused window to a workspace" descriptions[38]="Print the current random splash"
descriptions[41]="Temporarily enable or disable binds:ignore_group_lock" descriptions[39]="Execute a raw shell command"
descriptions[42]="List all workspaces with their properties" descriptions[40]="List active outputs with their properties"
descriptions[43]="Swap the active window with the next or previous in a group" descriptions[43]="Disable output"
descriptions[44]="Close a specified window" descriptions[44]="Gets the current config info about animations and beziers"
descriptions[45]="WARNING" descriptions[47]="Change the split ratio"
descriptions[46]="Specify the Hyprland instance" descriptions[48]="Move the active window"
descriptions[47]="List all registered binds" descriptions[49]="Pass the key to a specified window"
descriptions[48]="Move the active window in a direction or to a monitor" descriptions[50]="Swap the focused window with the next window"
descriptions[49]="Change the split ratio" descriptions[51]="List all connected keyboards and mice"
descriptions[51]="Prohibit the active window from becoming or being inserted into group" descriptions[52]="List the layers"
descriptions[52]="Change the workspace" descriptions[54]="Lock the focused group"
descriptions[53]="List all current config parsing errors" descriptions[55]="OK"
descriptions[54]="Toggle the current active window into a group" descriptions[56]="Move a workspace to a monitor"
descriptions[55]="Get the config option status (values)" descriptions[58]="Specify the Hyprland instance"
descriptions[58]="Close the active window" descriptions[59]="Disable output"
descriptions[59]="Pass the key to a specified window" descriptions[61]="Pin a window"
descriptions[60]="List all decorations and their info" descriptions[62]="WARNING"
descriptions[61]="List all connected keyboards and mice" descriptions[63]="INFO"
descriptions[62]="Switch focus from current to previously focused window" descriptions[66]="Set the current window's floating state to true"
descriptions[63]="Change the current mapping group" descriptions[69]="On shortcut X sends shortcut Y to a specified window"
descriptions[64]="Execute a Global Shortcut using the GlobalShortcuts portal" descriptions[70]="List all workspaces with their properties"
descriptions[66]="Force the renderer to reload all resources and outputs" descriptions[71]="Focus the next window on a workspace"
descriptions[67]="Move a selected window" descriptions[72]="Modify the window stack order of the active or specified window"
descriptions[69]="Print the Hyprland version: flags, commit and branch of build" descriptions[73]="Toggle the current active window into a group"
descriptions[70]="Set all monitors' DPMS status" descriptions[74]="Lock the groups"
descriptions[71]="Resize the active window" descriptions[76]="Set all monitors' DPMS status"
descriptions[72]="Move the active window into a group" descriptions[77]="Switch focus from current to previously focused window"
descriptions[73]="OK" descriptions[78]="No Icon"
descriptions[75]="Set the current window's floating state to true" descriptions[79]="Execute a batch of commands separated by ;"
descriptions[76]="Print tail of the log" descriptions[80]="Send a notification using the built-in Hyprland notification system"
descriptions[79]="List all layouts available (including plugin ones)" descriptions[82]="List all running Hyprland instances and their info"
descriptions[80]="Move a workspace to a monitor" descriptions[83]="Maximize no fullscreen"
descriptions[81]="Execute a shell command" descriptions[84]="Maximize and fullscreen"
descriptions[83]="Modify the window stack order of the active or specified window" descriptions[85]="Move the active window out of a group"
descriptions[84]="Toggle the focused window's internal fullscreen state" descriptions[86]="Close the active window"
descriptions[86]="Issue a keyword to call a config keyword dynamically" descriptions[87]="HINT"
descriptions[89]="Disable output" descriptions[88]="Move the focused window to a workspace"
descriptions[90]="Pin a window" descriptions[89]="Move the cursor to a specified position"
descriptions[91]="Allows adding/removing fake outputs to a specific backend" descriptions[90]="List all current config parsing errors"
descriptions[93]="Toggle a special workspace on/off" descriptions[91]="Close a specified window"
descriptions[94]="Toggle the focused window's fullscreen state" descriptions[92]="Swap the active window with another window"
descriptions[95]="Toggle the current window to always be opaque" descriptions[93]="Apply a tag to the window"
descriptions[96]="Focus the requested workspace" descriptions[94]="Force the renderer to reload all resources and outputs"
descriptions[98]="Switch to the next window in a group" descriptions[95]="Center the active window"
descriptions[99]="Output in JSON format" descriptions[97]="Focus the first window matching"
descriptions[100]="List all running Hyprland instances and their info" descriptions[98]="Set the hyprctl error string"
descriptions[101]="Execute a raw shell command" descriptions[101]="List all registered binds"
descriptions[102]="Exit the compositor with no questions asked" descriptions[102]="Print the Hyprland version: flags, commit and branch of build"
descriptions[103]="List all windows with their properties" descriptions[103]="Prints the help message"
descriptions[105]="Execute a batch of commands separated by ;" descriptions[104]="Toggle a special workspace on/off"
descriptions[106]="Dismiss all or up to amount of notifications" descriptions[105]="Toggle the focused window's fullscreen state"
descriptions[108]="Set the xkb layout index for a keyboard" descriptions[107]="None"
descriptions[109]="Move window doesnt switch to the workspace" descriptions[108]="Issue a keyword to call a config keyword dynamically"
descriptions[110]="Apply a tag to the window" descriptions[109]="Toggle the current window to always be opaque"
descriptions[111]="Behave as moveintogroup" descriptions[110]="ERROR"
descriptions[112]="Refresh state after issuing the command" descriptions[111]="Specify the Hyprland instance"
descriptions[113]="Move the focus in a direction" descriptions[112]="Toggle the current window's floating state"
descriptions[114]="Focus the urgent window or the last window" descriptions[113]="Rename a workspace"
descriptions[116]="Get the active workspace name and its properties" descriptions[115]="Get the active workspace name and its properties"
descriptions[117]="Issue a dispatch to call a keybind dispatcher with an arg" descriptions[117]="Get into a kill mode, where you can kill an app by clicking on it"
descriptions[119]="Center the active window" descriptions[119]="Allows adding/removing fake outputs to a specific backend"
descriptions[120]="HINT" descriptions[120]="Execute a Global Shortcut using the GlobalShortcuts portal"
descriptions[121]="Interact with hyprpaper if present" descriptions[121]="Issue a dispatch to call a keybind dispatcher with an arg"
descriptions[122]="No Icon" descriptions[122]="Force reload the config"
descriptions[123]="Force reload the config" descriptions[124]="Output in JSON format"
descriptions[125]="Print system info" descriptions[125]="Emits a custom event to socket2"
descriptions[126]="Interact with a plugin" descriptions[126]="Prints the help message"
descriptions[128]="Get the active window name and its properties" descriptions[128]="Current"
descriptions[129]="Swap the active workspaces between two monitors" descriptions[129]="Get the active window name and its properties"
descriptions[130]="Print the current random splash" descriptions[131]="Dismiss all or up to amount of notifications"
descriptions[131]="On shortcut X sends shortcut Y to a specified window" descriptions[132]="Focus a monitor"
descriptions[133]="Lock the focused group" descriptions[133]="Move the focus in a direction"
descriptions[136]="Lock the groups" descriptions[134]="Interact with a plugin"
descriptions[137]="Move the cursor to the corner of the active window" descriptions[135]="Exit the compositor with no questions asked"
descriptions[140]="INFO" descriptions[136]="Change the workspace"
descriptions[141]="Resize a selected window" descriptions[137]="Sets the focused windows fullscreen mode and the one sent to the client"
descriptions[138]="Get the config option status (values)"
descriptions[141]="List all decorations and their info"
descriptions[142]="Set the current window's floating state to false"
descriptions[144]="Return a parsable JSON with all the config options, descriptions, value types and ranges"
descriptions[145]="Resize a selected window"
descriptions[146]="Toggle the focused window's internal fullscreen state"
descriptions[147]="Print tail of the log"
descriptions[148]="Swap the active workspaces between two monitors"
descriptions[149]="Change the current mapping group"
descriptions[151]="Behave as moveintogroup"
descriptions[152]="Get the current cursor pos in global layout coordinates"
descriptions[154]="Focus the requested workspace"
local -A literal_transitions local -A literal_transitions
literal_transitions[1]="([106]=2 [76]=3 [34]=4 [36]=5 [2]=3 [3]=3 [79]=3 [108]=6 [38]=3 [112]=5 [42]=3 [47]=3 [116]=3 [86]=7 [117]=9 [53]=3 [89]=5 [55]=3 [91]=10 [121]=3 [123]=3 [125]=3 [16]=3 [60]=11 [61]=3 [18]=12 [126]=13 [20]=3 [128]=3 [130]=3 [26]=14 [69]=3 [99]=5 [100]=3 [28]=3 [29]=15 [103]=3 [105]=5)" literal_transitions[1]="([121]=15 [44]=3 [126]=22 [82]=3 [4]=22 [52]=3 [51]=3 [129]=3 [90]=3 [59]=22 [9]=3 [11]=3 [12]=4 [131]=5 [14]=6 [98]=7 [102]=3 [103]=22 [134]=8 [101]=3 [138]=3 [23]=3 [20]=3 [141]=9 [26]=3 [144]=3 [108]=10 [147]=11 [70]=3 [34]=3 [35]=3 [79]=22 [115]=3 [38]=3 [152]=3 [117]=3 [122]=14 [124]=22 [40]=12 [43]=22 [80]=16 [119]=13)"
literal_transitions[4]="([74]=18 [14]=3 [33]=18 [56]=18 [57]=18 [92]=18 [107]=3 [124]=3 [78]=2 [17]=3 [127]=18 [4]=2 [6]=3 [65]=18 [132]=3 [134]=18 [82]=18 [135]=18 [85]=18 [32]=18 [50]=3 [13]=3 [87]=18 [11]=18 [88]=18 [142]=18)" literal_transitions[2]="([82]=3 [52]=3 [51]=3 [129]=3 [9]=3 [90]=3 [11]=3 [12]=4 [131]=5 [14]=6 [98]=7 [102]=3 [134]=8 [101]=3 [23]=3 [20]=3 [138]=3 [141]=9 [26]=3 [144]=3 [108]=10 [147]=11 [70]=3 [34]=3 [35]=3 [115]=3 [38]=3 [152]=3 [117]=3 [40]=12 [119]=13 [122]=14 [121]=15 [80]=16 [44]=3)"
literal_transitions[8]="([106]=2 [76]=3 [34]=4 [2]=3 [3]=3 [79]=3 [108]=6 [38]=3 [42]=3 [47]=3 [116]=3 [86]=7 [117]=9 [53]=3 [55]=3 [91]=10 [121]=3 [123]=3 [125]=3 [16]=3 [60]=11 [61]=3 [18]=12 [126]=13 [20]=3 [128]=3 [130]=3 [26]=14 [69]=3 [100]=3 [28]=3 [29]=15 [103]=3)" literal_transitions[4]="([140]=3 [64]=17 [65]=17 [46]=17 [106]=17 [28]=3 [27]=3 [53]=5 [6]=17 [67]=3 [68]=17 [130]=17 [114]=17 [13]=3 [75]=5 [100]=3 [36]=17 [153]=17 [99]=17 [60]=17 [118]=17 [42]=17 [18]=3 [139]=17 [155]=3 [123]=17)"
literal_transitions[9]="([102]=3 [131]=3 [133]=3 [1]=3 [75]=3 [37]=3 [109]=3 [110]=3 [39]=3 [111]=3 [5]=3 [80]=3 [41]=3 [81]=3 [114]=3 [7]=3 [43]=3 [44]=3 [83]=3 [84]=3 [48]=3 [49]=3 [10]=3 [51]=3 [52]=3 [54]=3 [12]=3 [113]=3 [90]=3 [119]=3 [58]=3 [93]=3 [59]=3 [94]=3 [95]=3 [62]=3 [63]=3 [129]=3 [96]=3 [64]=3 [21]=3 [98]=3 [23]=3 [24]=3 [66]=3 [67]=3 [136]=3 [137]=3 [25]=3 [27]=3 [70]=3 [101]=3 [71]=3 [141]=3 [30]=3 [72]=3)" literal_transitions[7]="([127]=3)"
literal_transitions[10]="([118]=21 [115]=17)" literal_transitions[11]="([57]=3)"
literal_transitions[12]="([104]=3)" literal_transitions[12]="([10]=3)"
literal_transitions[14]="([22]=2 [120]=2 [31]=2 [140]=2 [122]=2 [45]=2 [73]=2)" literal_transitions[13]="([15]=20 [81]=23)"
literal_transitions[15]="([40]=3)" literal_transitions[14]="([143]=3)"
literal_transitions[16]="([139]=3 [97]=3)" literal_transitions[15]="([1]=3 [85]=3 [3]=3 [86]=3 [5]=3 [88]=3 [89]=3 [91]=3 [92]=3 [93]=3 [94]=3 [95]=3 [97]=3 [16]=3 [19]=3 [104]=3 [22]=3 [105]=3 [24]=3 [25]=3 [29]=3 [30]=3 [31]=3 [109]=3 [112]=3 [33]=3 [113]=3 [37]=3 [39]=3 [120]=3 [125]=3 [47]=3 [48]=3 [49]=3 [50]=3 [54]=3 [56]=3 [132]=3 [133]=3 [135]=3 [136]=3 [61]=3 [137]=21 [142]=3 [66]=3 [145]=3 [146]=3 [69]=3 [148]=3 [71]=3 [72]=3 [73]=3 [74]=3 [149]=3 [76]=3 [77]=3 [151]=3 [154]=3)"
literal_transitions[18]="([19]=3 [8]=3)" literal_transitions[16]="([87]=5 [7]=5 [110]=5 [62]=5 [78]=5 [55]=5 [63]=5)"
literal_transitions[19]="([77]=20)" literal_transitions[17]="([41]=3 [45]=3)"
literal_transitions[20]="([35]=5 [46]=5)" literal_transitions[18]="([8]=24)"
literal_transitions[21]="([9]=3 [68]=3 [15]=3 [138]=3)" literal_transitions[19]="([32]=3 [150]=3)"
literal_transitions[20]="([96]=3 [17]=3 [116]=3 [21]=3)"
literal_transitions[21]="([107]=3 [83]=3 [128]=3 [2]=3 [84]=3)"
literal_transitions[24]="([58]=22 [111]=22)"
local -A match_anything_transitions local -A match_anything_transitions
match_anything_transitions=([2]=3 [1]=8 [7]=3 [16]=3 [11]=3 [6]=16 [15]=19 [8]=8 [3]=19 [17]=3 [13]=3 [12]=19) match_anything_transitions=([7]=18 [8]=3 [1]=2 [23]=3 [6]=19 [5]=3 [3]=18 [19]=3 [12]=18 [9]=3 [10]=3 [14]=18 [11]=18 [2]=2)
declare -A subword_transitions declare -A subword_transitions
@ -199,7 +214,7 @@ _hyprctl () {
fi fi
done done
fi fi
local -A commands=([6]=1 [17]=2 [13]=3 [11]=0) local -A commands=([8]=0 [23]=1 [9]=3 [6]=2)
if [[ -v "commands[$state]" ]]; then if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]} local command_id=${commands[$state]}
@ -252,4 +267,8 @@ _hyprctl () {
return 0 return 0
} }
if [[ $ZSH_EVAL_CONTEXT =~ :file$ ]]; then
compdef _hyprctl hyprctl compdef _hyprctl hyprctl
else
_hyprctl
fi

View file

@ -17,6 +17,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <print>
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -26,6 +27,7 @@
#include <regex> #include <regex>
#include <sys/socket.h> #include <sys/socket.h>
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <cstring>
using namespace Hyprutils::String; using namespace Hyprutils::String;
#include "Strings.hpp" #include "Strings.hpp"
@ -43,11 +45,11 @@ struct SInstanceData {
bool valid = true; bool valid = true;
}; };
void log(std::string str) { void log(const std::string& str) {
if (quiet) if (quiet)
return; return;
std::cout << str << "\n"; std::println("{}", str);
} }
std::string getRuntimeDir() { std::string getRuntimeDir() {
@ -104,7 +106,7 @@ std::vector<SInstanceData> instances() {
static volatile bool sigintReceived = false; static volatile bool sigintReceived = false;
void intHandler(int sig) { void intHandler(int sig) {
sigintReceived = true; sigintReceived = true;
std::cout << "[hyprctl] SIGINT received, closing connection" << std::endl; std::println("[hyprctl] SIGINT received, closing connection");
} }
int rollingRead(const int socket) { int rollingRead(const int socket) {
@ -113,13 +115,13 @@ int rollingRead(const int socket) {
constexpr size_t BUFFER_SIZE = 8192; constexpr size_t BUFFER_SIZE = 8192;
std::array<char, BUFFER_SIZE> buffer = {0}; std::array<char, BUFFER_SIZE> buffer = {0};
int sizeWritten = 0; long sizeWritten = 0;
std::cout << "[hyprctl] reading from socket following up log:" << std::endl; std::println("[hyprctl] reading from socket following up log:");
while (!sigintReceived) { while (!sigintReceived) {
sizeWritten = read(socket, buffer.data(), BUFFER_SIZE); sizeWritten = read(socket, buffer.data(), BUFFER_SIZE);
if (sizeWritten < 0 && errno != EAGAIN) { if (sizeWritten < 0 && errno != EAGAIN) {
if (errno != EINTR) if (errno != EINTR)
std::cout << "Couldn't read (5) " << strerror(errno) << ":" << errno << std::endl; std::println("Couldn't read (5): {}: {}", strerror(errno), errno);
close(socket); close(socket);
return 5; return 5;
} }
@ -128,7 +130,7 @@ int rollingRead(const int socket) {
break; break;
if (sizeWritten > 0) { if (sizeWritten > 0) {
std::cout << std::string(buffer.data(), sizeWritten); std::println("{}", std::string(buffer.data(), sizeWritten));
buffer.fill('\0'); buffer.fill('\0');
} }
@ -286,12 +288,12 @@ void instancesRequest(bool json) {
std::vector<SInstanceData> inst = instances(); std::vector<SInstanceData> inst = instances();
if (!json) { if (!json) {
for (auto& el : inst) { for (auto const& el : inst) {
result += std::format("instance {}:\n\ttime: {}\n\tpid: {}\n\twl socket: {}\n\n", el.id, el.time, el.pid, el.wlSocket); result += std::format("instance {}:\n\ttime: {}\n\tpid: {}\n\twl socket: {}\n\n", el.id, el.time, el.pid, el.wlSocket);
} }
} else { } else {
result += '['; result += '[';
for (auto& el : inst) { for (auto const& el : inst) {
result += std::format(R"#( result += std::format(R"#(
{{ {{
"instance": "{}", "instance": "{}",
@ -322,7 +324,7 @@ int main(int argc, char** argv) {
bool parseArgs = true; bool parseArgs = true;
if (argc < 2) { if (argc < 2) {
std::cout << USAGE << std::endl; std::println("{}", USAGE);
return 1; return 1;
} }
@ -359,7 +361,7 @@ int main(int argc, char** argv) {
++i; ++i;
if (i >= ARGS.size()) { if (i >= ARGS.size()) {
std::cout << USAGE << std::endl; std::println("{}", USAGE);
return 1; return 1;
} }
@ -370,24 +372,24 @@ int main(int argc, char** argv) {
const std::string& cmd = ARGS[0]; const std::string& cmd = ARGS[0];
if (cmd == "hyprpaper") { if (cmd == "hyprpaper") {
std::cout << HYPRPAPER_HELP << std::endl; std::println("{}", HYPRPAPER_HELP);
} else if (cmd == "notify") { } else if (cmd == "notify") {
std::cout << NOTIFY_HELP << std::endl; std::println("{}", NOTIFY_HELP);
} else if (cmd == "output") { } else if (cmd == "output") {
std::cout << OUTPUT_HELP << std::endl; std::println("{}", OUTPUT_HELP);
} else if (cmd == "plugin") { } else if (cmd == "plugin") {
std::cout << PLUGIN_HELP << std::endl; std::println("{}", PLUGIN_HELP);
} else if (cmd == "setprop") { } else if (cmd == "setprop") {
std::cout << SETPROP_HELP << std::endl; std::println("{}", SETPROP_HELP);
} else if (cmd == "switchxkblayout") { } else if (cmd == "switchxkblayout") {
std::cout << SWITCHXKBLAYOUT_HELP << std::endl; std::println("{}", SWITCHXKBLAYOUT_HELP);
} else { } else {
std::cout << USAGE << std::endl; std::println("{}", USAGE);
} }
return 1; return 1;
} else { } else {
std::cout << USAGE << std::endl; std::println("{}", USAGE);
return 1; return 1;
} }
@ -398,7 +400,7 @@ int main(int argc, char** argv) {
} }
if (fullRequest.empty()) { if (fullRequest.empty()) {
std::cout << USAGE << std::endl; std::println("{}", USAGE);
return 1; return 1;
} }
@ -475,7 +477,7 @@ int main(int argc, char** argv) {
else if (fullRequest.contains("/decorations")) else if (fullRequest.contains("/decorations"))
exitStatus = request(fullRequest, 1); exitStatus = request(fullRequest, 1);
else if (fullRequest.contains("/--help")) else if (fullRequest.contains("/--help"))
std::cout << USAGE << std::endl; std::println("{}", USAGE);
else if (fullRequest.contains("/rollinglog") && needRoll) else if (fullRequest.contains("/rollinglog") && needRoll)
exitStatus = request(fullRequest, 0, true); exitStatus = request(fullRequest, 0, true);
else { else {

View file

@ -1,10 +1,26 @@
executable('hyprctl', 'main.cpp', executable(
'hyprctl',
'main.cpp',
dependencies: [ dependencies: [
dependency('hyprutils', version: '>= 0.1.1'), dependency('hyprutils', version: '>= 0.1.1'),
], ],
install: true install: true,
) )
install_data('hyprctl.bash', install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), install_tag: 'runtime', rename: 'hyprctl') install_data(
install_data('hyprctl.fish', install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), install_tag: 'runtime') 'hyprctl.bash',
install_data('hyprctl.zsh', install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), install_tag: 'runtime', rename: '_hyprctl') install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
install_tag: 'runtime',
rename: 'hyprctl',
)
install_data(
'hyprctl.fish',
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
install_tag: 'runtime',
)
install_data(
'hyprctl.zsh',
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
install_tag: 'runtime',
rename: '_hyprctl',
)

View file

@ -2,6 +2,10 @@ _hyprpm_cmd_0 () {
hyprpm list | awk '/Plugin/{print $4}' hyprpm list | awk '/Plugin/{print $4}'
} }
_hyprpm_cmd_1 () {
hyprpm list | awk '/Repository/{print $4}' | sed 's/:$//'
}
_hyprpm () { _hyprpm () {
if [[ $(type -t _get_comp_words_by_ref) != function ]]; then if [[ $(type -t _get_comp_words_by_ref) != function ]]; then
echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed echo _get_comp_words_by_ref: function not defined. Make sure the bash-completions system package is installed
@ -11,16 +15,13 @@ _hyprpm () {
local words cword local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword _get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
local -a literals=("-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f") declare -a literals=(--no-shallow -n ::= disable list --help update add --verbose -v --force -s remove enable --notify -h reload -f)
declare -A literal_transitions declare -A literal_transitions
literal_transitions[0]="([9]=6 [2]=2 [7]=6 [8]=6 [4]=6 [10]=2 [11]=3 [5]=2 [13]=6 [3]=3 [14]=2 [15]=6 [6]=2)" literal_transitions[0]="([0]=7 [3]=3 [4]=4 [8]=7 [9]=7 [6]=4 [7]=4 [11]=7 [5]=7 [10]=7 [12]=2 [13]=3 [15]=7 [16]=4 [17]=7)"
literal_transitions[1]="([10]=2 [11]=3 [3]=3 [2]=2 [14]=2 [5]=2 [6]=2)" literal_transitions[1]="([12]=2 [13]=3 [3]=3 [4]=4 [16]=4 [6]=4 [7]=4)"
literal_transitions[4]="([1]=5)" literal_transitions[5]="([2]=6)"
literal_transitions[5]="([0]=6 [12]=6)" literal_transitions[6]="([1]=7 [14]=7)"
declare -A match_anything_transitions=([1]=1 [4]=5 [3]=4 [2]=4 [0]=1)
declare -A match_anything_transitions
match_anything_transitions=([3]=2 [2]=4 [0]=1 [1]=1)
declare -A subword_transitions declare -A subword_transitions
local state=0 local state=0
@ -58,21 +59,9 @@ _hyprpm () {
done done
local -a matches=()
local prefix="${words[$cword]}" local prefix="${words[$cword]}"
local shortest_suffix="$word"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate="${word##*$char}"
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$word" ]]; then
local superfluous_prefix=${word%$shortest_suffix}
fi
if [[ -v "literal_transitions[$state]" ]]; then if [[ -v "literal_transitions[$state]" ]]; then
local state_transitions_initializer=${literal_transitions[$state]} local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions declare -A state_transitions
@ -81,25 +70,38 @@ _hyprpm () {
for literal_id in "${!state_transitions[@]}"; do for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}" local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"} matches+=("$literal ")
COMPREPLY+=("$completion ")
fi fi
done done
fi fi
declare -A commands declare -A commands
commands=([3]=0) commands=([3]=0 [2]=1)
if [[ -v "commands[$state]" ]]; then if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]} local command_id=${commands[$state]}
local completions=() local completions=()
mapfile -t completions < <(_hyprpm_cmd_${command_id} "$prefix" | cut -f1) readarray -t completions < <(_hyprpm_cmd_${command_id} "$prefix" | cut -f1)
for item in "${completions[@]}"; do for item in "${completions[@]}"; do
if [[ $item = "${prefix}"* ]]; then if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item") matches+=("$item")
fi fi
done done
fi fi
local shortest_suffix="$prefix"
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do
local char="${COMP_WORDBREAKS:$i:1}"
local candidate=${prefix##*$char}
if [[ ${#candidate} -lt ${#shortest_suffix} ]]; then
shortest_suffix=$candidate
fi
done
local superfluous_prefix=""
if [[ "$shortest_suffix" != "$prefix" ]]; then
local superfluous_prefix=${prefix%$shortest_suffix}
fi
COMPREPLY=("${matches[@]#$superfluous_prefix}")
return 0 return 0
} }

View file

@ -3,6 +3,11 @@ function _hyprpm_1
hyprpm list | awk '/Plugin/{print $4}' hyprpm list | awk '/Plugin/{print $4}'
end end
function _hyprpm_2
set 1 $argv[1]
hyprpm list | awk '/Repository/{print $4}' | sed 's/:$//'
end
function _hyprpm function _hyprpm
set COMP_LINE (commandline --cut-at-cursor) set COMP_LINE (commandline --cut-at-cursor)
@ -14,49 +19,51 @@ function _hyprpm
set COMP_CWORD (count $COMP_WORDS) set COMP_CWORD (count $COMP_WORDS)
end end
set --local literals "-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f" set literals "--no-shallow" "-n" "::=" "disable" "list" "--help" "update" "add" "--verbose" "-v" "--force" "-s" "remove" "enable" "--notify" "-h" "reload" "-f"
set --local descriptions set descriptions
set descriptions[1] "Send a hyprland notification for important events (e.g. load fail)" set descriptions[1] "Disable shallow cloning of Hyprland sources"
set descriptions[3] "List all installed plugins" set descriptions[2] "Send a hyprland notification for important events (e.g. load fail)"
set descriptions[4] "Unload a plugin" set descriptions[4] "Unload a plugin"
set descriptions[5] "Show help menu" set descriptions[5] "List all installed plugins"
set descriptions[6] "Check and update all plugins if needed" set descriptions[6] "Show help menu"
set descriptions[7] "Install a new plugin repository from git" set descriptions[7] "Check and update all plugins if needed"
set descriptions[8] "Enable too much loggin" set descriptions[8] "Install a new plugin repository from git"
set descriptions[9] "Enable too much loggin" set descriptions[9] "Enable too much loggin"
set descriptions[10] "Force an operation ignoring checks (e.g. update -f)" set descriptions[10] "Enable too much loggin"
set descriptions[11] "Remove a plugin repository" set descriptions[11] "Force an operation ignoring checks (e.g. update -f)"
set descriptions[12] "Load a plugin" set descriptions[12] "Disable shallow cloning of Hyprland sources"
set descriptions[13] "Send a hyprland notification for important events (e.g. load fail)" set descriptions[13] "Remove a plugin repository"
set descriptions[14] "Show help menu" set descriptions[14] "Load a plugin"
set descriptions[15] "Reload all plugins" set descriptions[15] "Send a hyprland notification for important events (e.g. load fail)"
set descriptions[16] "Force an operation ignoring checks (e.g. update -f)" set descriptions[16] "Show help menu"
set descriptions[17] "Reload all plugins"
set descriptions[18] "Force an operation ignoring checks (e.g. update -f)"
set --local literal_transitions set literal_transitions
set literal_transitions[1] "set inputs 10 3 8 9 5 11 12 6 14 4 15 16 7; set tos 7 3 7 7 7 3 4 3 7 4 3 7 3" set literal_transitions[1] "set inputs 1 4 5 9 10 7 8 12 6 11 13 14 16 17 18; set tos 8 4 5 8 8 5 5 8 8 8 3 4 8 5 8"
set literal_transitions[2] "set inputs 11 12 4 3 15 6 7; set tos 3 4 4 3 3 3 3" set literal_transitions[2] "set inputs 13 14 4 5 17 7 8; set tos 3 4 4 5 5 5 5"
set literal_transitions[5] "set inputs 2; set tos 6" set literal_transitions[6] "set inputs 3; set tos 7"
set literal_transitions[6] "set inputs 1 13; set tos 7 7" set literal_transitions[7] "set inputs 2 15; set tos 8 8"
set --local match_anything_transitions_from 4 3 1 2 set match_anything_transitions_from 2 5 4 3 1
set --local match_anything_transitions_to 3 5 2 2 set match_anything_transitions_to 2 6 5 5 2
set --local state 1 set state 1
set --local word_index 2 set word_index 2
while test $word_index -lt $COMP_CWORD while test $word_index -lt $COMP_CWORD
set --local -- word $COMP_WORDS[$word_index] set -- word $COMP_WORDS[$word_index]
if set --query literal_transitions[$state] && test -n $literal_transitions[$state] if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
eval $literal_transitions[$state] eval $literal_transitions[$state]
if contains -- $word $literals if contains -- $word $literals
set --local literal_matched 0 set literal_matched 0
for literal_id in (seq 1 (count $literals)) for literal_id in (seq 1 (count $literals))
if test $literals[$literal_id] = $word if test $literals[$literal_id] = $word
set --local index (contains --index -- $literal_id $inputs) set index (contains --index -- $literal_id $inputs)
set state $tos[$index] set state $tos[$index]
set word_index (math $word_index + 1) set word_index (math $word_index + 1)
set literal_matched 1 set literal_matched 1
@ -70,7 +77,7 @@ function _hyprpm
end end
if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state] if set --query match_anything_transitions_from[$state] && test -n $match_anything_transitions_from[$state]
set --local index (contains --index -- $state $match_anything_transitions_from) set index (contains --index -- $state $match_anything_transitions_from)
set state $match_anything_transitions_to[$index] set state $match_anything_transitions_to[$index]
set word_index (math $word_index + 1) set word_index (math $word_index + 1)
continue continue
@ -80,8 +87,8 @@ function _hyprpm
end end
if set --query literal_transitions[$state] && test -n $literal_transitions[$state] if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
eval $literal_transitions[$state] eval $literal_transitions[$state]
for literal_id in $inputs for literal_id in $inputs
if test -n $descriptions[$literal_id] if test -n $descriptions[$literal_id]
@ -92,14 +99,14 @@ function _hyprpm
end end
end end
set command_states 4 set command_states 4 3
set command_ids 1 set command_ids 1 2
if contains $state $command_states if contains $state $command_states
set --local index (contains --index $state $command_states) set index (contains --index $state $command_states)
set --local function_id $command_ids[$index] set function_id $command_ids[$index]
set --local function_name _hyprpm_$function_id set function_name _hyprpm_$function_id
set --local --erase inputs set --erase inputs
set --local --erase tos set --erase tos
$function_name "$COMP_WORDS[$COMP_CWORD]" $function_name "$COMP_WORDS[$COMP_CWORD]"
end end

View file

@ -5,10 +5,11 @@ hyprpm [<FLAGS>]... <ARGUMENT>
| (--help | -h) "Show help menu" | (--help | -h) "Show help menu"
| (--verbose | -v) "Enable too much loggin" | (--verbose | -v) "Enable too much loggin"
| (--force | -f) "Force an operation ignoring checks (e.g. update -f)" | (--force | -f) "Force an operation ignoring checks (e.g. update -f)"
| (--no-shallow | -s) "Disable shallow cloning of Hyprland sources"
; ;
<ARGUMENT> ::= (add) "Install a new plugin repository from git" <ARGUMENT> ::= (add) "Install a new plugin repository from git"
| (remove) "Remove a plugin repository" | (remove <PLUGIN_REPOS>) "Remove a plugin repository"
| (update) "Check and update all plugins if needed" | (update) "Check and update all plugins if needed"
| (list) "List all installed plugins" | (list) "List all installed plugins"
| (enable <PLUGINS>) "Load a plugin" | (enable <PLUGINS>) "Load a plugin"
@ -17,3 +18,4 @@ hyprpm [<FLAGS>]... <ARGUMENT>
; ;
<PLUGINS> ::= {{{ hyprpm list | awk '/Plugin/{print $4}' }}}; <PLUGINS> ::= {{{ hyprpm list | awk '/Plugin/{print $4}' }}};
<PLUGIN_REPOS> ::= {{{ hyprpm list | awk '/Repository/{print $4}' | sed 's/:$//' }}};

View file

@ -4,34 +4,40 @@ _hyprpm_cmd_0 () {
hyprpm list | awk '/Plugin/{print $4}' hyprpm list | awk '/Plugin/{print $4}'
} }
_hyprpm_cmd_1 () {
hyprpm list | awk '/Repository/{print $4}' | sed 's/:$//'
}
_hyprpm () { _hyprpm () {
local -a literals=("-n" "::=" "list" "disable" "--help" "update" "add" "--verbose" "-v" "--force" "remove" "enable" "--notify" "-h" "reload" "-f") local -a literals=("--no-shallow" "-n" "::=" "disable" "list" "--help" "update" "add" "--verbose" "-v" "--force" "-s" "remove" "enable" "--notify" "-h" "reload" "-f")
local -A descriptions local -A descriptions
descriptions[1]="Send a hyprland notification for important events (e.g. load fail)" descriptions[1]="Disable shallow cloning of Hyprland sources"
descriptions[3]="List all installed plugins" descriptions[2]="Send a hyprland notification for important events (e.g. load fail)"
descriptions[4]="Unload a plugin" descriptions[4]="Unload a plugin"
descriptions[5]="Show help menu" descriptions[5]="List all installed plugins"
descriptions[6]="Check and update all plugins if needed" descriptions[6]="Show help menu"
descriptions[7]="Install a new plugin repository from git" descriptions[7]="Check and update all plugins if needed"
descriptions[8]="Enable too much loggin" descriptions[8]="Install a new plugin repository from git"
descriptions[9]="Enable too much loggin" descriptions[9]="Enable too much loggin"
descriptions[10]="Force an operation ignoring checks (e.g. update -f)" descriptions[10]="Enable too much loggin"
descriptions[11]="Remove a plugin repository" descriptions[11]="Force an operation ignoring checks (e.g. update -f)"
descriptions[12]="Load a plugin" descriptions[12]="Disable shallow cloning of Hyprland sources"
descriptions[13]="Send a hyprland notification for important events (e.g. load fail)" descriptions[13]="Remove a plugin repository"
descriptions[14]="Show help menu" descriptions[14]="Load a plugin"
descriptions[15]="Reload all plugins" descriptions[15]="Send a hyprland notification for important events (e.g. load fail)"
descriptions[16]="Force an operation ignoring checks (e.g. update -f)" descriptions[16]="Show help menu"
descriptions[17]="Reload all plugins"
descriptions[18]="Force an operation ignoring checks (e.g. update -f)"
local -A literal_transitions local -A literal_transitions
literal_transitions[1]="([10]=7 [3]=3 [8]=7 [9]=7 [5]=7 [11]=3 [12]=4 [6]=3 [14]=7 [4]=4 [15]=3 [16]=7 [7]=3)" literal_transitions[1]="([1]=8 [4]=4 [5]=5 [9]=8 [10]=8 [7]=5 [8]=5 [12]=8 [6]=8 [11]=8 [13]=3 [14]=4 [16]=8 [17]=5 [18]=8)"
literal_transitions[2]="([11]=3 [12]=4 [4]=4 [3]=3 [15]=3 [6]=3 [7]=3)" literal_transitions[2]="([13]=3 [14]=4 [4]=4 [5]=5 [17]=5 [7]=5 [8]=5)"
literal_transitions[5]="([2]=6)" literal_transitions[6]="([3]=7)"
literal_transitions[6]="([1]=7 [13]=7)" literal_transitions[7]="([2]=8 [15]=8)"
local -A match_anything_transitions local -A match_anything_transitions
match_anything_transitions=([4]=3 [3]=5 [1]=2 [2]=2) match_anything_transitions=([2]=2 [5]=6 [4]=5 [3]=5 [1]=2)
declare -A subword_transitions declare -A subword_transitions
@ -91,7 +97,7 @@ _hyprpm () {
fi fi
done done
fi fi
local -A commands=([4]=0) local -A commands=([4]=0 [3]=1)
if [[ -v "commands[$state]" ]]; then if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]} local command_id=${commands[$state]}

View file

@ -1,6 +1,6 @@
#include "DataState.hpp" #include "DataState.hpp"
#include <toml++/toml.hpp> #include <toml++/toml.hpp>
#include <iostream> #include <print>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include "PluginManager.hpp" #include "PluginManager.hpp"
@ -8,7 +8,7 @@
std::string DataState::getDataStatePath() { std::string DataState::getDataStatePath() {
const auto HOME = getenv("HOME"); const auto HOME = getenv("HOME");
if (!HOME) { if (!HOME) {
std::cerr << "DataState: no $HOME\n"; std::println(stderr, "DataState: no $HOME");
throw std::runtime_error("no $HOME"); throw std::runtime_error("no $HOME");
return ""; return "";
} }
@ -49,7 +49,7 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
{"rev", repo.rev} {"rev", repo.rev}
}} }}
}; };
for (auto& p : repo.plugins) { for (auto const& p : repo.plugins) {
// copy .so to the good place // copy .so to the good place
if (std::filesystem::exists(p.filename)) if (std::filesystem::exists(p.filename))
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so"); std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");

View file

@ -6,7 +6,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
auto manifest = toml::parse_file(path); auto manifest = toml::parse_file(path);
if (type == MANIFEST_HYPRLOAD) { if (type == MANIFEST_HYPRLOAD) {
for (auto& [key, val] : manifest) { for (auto const& [key, val] : manifest) {
if (key.str().ends_with(".build")) if (key.str().ends_with(".build"))
continue; continue;
@ -63,7 +63,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
} }
} }
for (auto& [key, val] : manifest) { for (auto const& [key, val] : manifest) {
if (key.str() == "repository") if (key.str() == "repository")
continue; continue;

View file

@ -1,12 +1,15 @@
#include "PluginManager.hpp" #include "PluginManager.hpp"
#include "../helpers/Colors.hpp" #include "../helpers/Colors.hpp"
#include "../helpers/StringUtils.hpp"
#include "../progress/CProgressBar.hpp" #include "../progress/CProgressBar.hpp"
#include "Manifest.hpp" #include "Manifest.hpp"
#include "DataState.hpp" #include "DataState.hpp"
#include <cstdio>
#include <iostream> #include <iostream>
#include <array> #include <array>
#include <filesystem> #include <filesystem>
#include <print>
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
@ -31,6 +34,7 @@ static std::string execAndGet(std::string cmd) {
if (!pipe) if (!pipe)
return ""; return "";
result.reserve(buffer.size());
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data(); result += buffer.data();
} }
@ -47,10 +51,10 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
once = true; once = true;
const auto HLVERCALL = execAndGet("hyprctl version"); const auto HLVERCALL = execAndGet("hyprctl version");
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "version returned: " << HLVERCALL << "\n"; std::println("{}", verboseString("version returned: {}", HLVERCALL));
if (!HLVERCALL.contains("Tag:")) { if (!HLVERCALL.contains("Tag:")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " You don't seem to be running Hyprland."; std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
return SHyprlandVersion{}; return SHyprlandVersion{};
} }
@ -76,7 +80,7 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
} catch (...) { ; } } catch (...) { ; }
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "parsed commit " << hlcommit << " at branch " << hlbranch << " on " << hldate << ", commits " << commits << "\n"; std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits}; ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
return ver; return ver;
@ -102,20 +106,21 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HLVER = getHyprlandVersion(); const auto HLVER = getHyprlandVersion();
if (!hasDeps()) { if (!hasDeps()) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio\n"; std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
return false; return false;
} }
if (DataState::pluginRepoExists(url)) { if (DataState::pluginRepoExists(url)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. Repository already installed.\n"; std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
return false; return false;
} }
auto GLOBALSTATE = DataState::getGlobalState(); auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) { if (!GLOBALSTATE.dontWarnInstall) {
std::cout << Colors::YELLOW << "!" << Colors::RED << " Disclaimer:\n " << Colors::RESET std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET);
<< "plugins, especially not official, have no guarantee of stability, availablity or security.\n Run them at your own risk.\n " std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n"
<< "This message will not appear again.\n"; "Run them at your own risk.\n"
"This message will not appear again.");
GLOBALSTATE.dontWarnInstall = true; GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE); DataState::updateGlobalState(GLOBALSTATE);
} }
@ -129,7 +134,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::getline(std::cin, input); std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') { if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n"; std::println(stderr, "Aborting.");
return false; return false;
} }
@ -144,7 +149,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::filesystem::create_directory("/tmp/hyprpm"); std::filesystem::create_directory("/tmp/hyprpm");
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace); std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
} else if (!std::filesystem::is_directory("/tmp/hyprpm")) { } else if (!std::filesystem::is_directory("/tmp/hyprpm")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for hyprpm\n"; std::println(stderr, "\n{}", failureString("Could not prepare working dir for hyprpm"));
return false; return false;
} }
@ -153,60 +158,60 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME; m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
if (!createSafeDirectory(m_szWorkingPluginDirectory)) { if (!createSafeDirectory(m_szWorkingPluginDirectory)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for repo\n"; std::println(stderr, "\n{}", failureString("Could not prepare working dir for repo"));
return false; return false;
} }
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + url); progress.printMessageAbove(infoString("Cloning {}", url));
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " " + USERNAME); std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + url + " " + USERNAME);
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the plugin repository. shell returned:\n" << ret << "\n"; std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
return false; return false;
} }
if (!rev.empty()) { if (!rev.empty()) {
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev); std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev);
if (ret.compare(0, 6, "fatal:") == 0) { if (ret.compare(0, 6, "fatal:") == 0) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not check out revision " << rev << ". shell returned:\n" << ret << "\n"; std::println(stderr, "\n{}", failureString("Could not check out revision {}. shell returned:\n{}", rev, ret));
return false; return false;
} }
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init"); ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n"; std::println("{}", verboseString("git submodule update --init returned: {}", ret));
} }
progress.m_iSteps = 1; progress.m_iSteps = 1;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned"); progress.printMessageAbove(successString("cloned"));
progress.m_szCurrentMessage = "Reading the manifest"; progress.m_szCurrentMessage = "Reading the manifest";
progress.print(); progress.print();
std::unique_ptr<CManifest> pManifest; std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) { if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest"); progress.printMessageAbove(successString("found hyprpm manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml"); pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) { } else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest"); progress.printMessageAbove(successString("found hyprload manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml"); pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
} }
if (!pManifest) { if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n"; std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
return false; return false;
} }
if (!pManifest->m_bGood) { if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n"; std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
return false; return false;
} }
progress.m_iSteps = 2; progress.m_iSteps = 2;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"); progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
for (auto& pl : pManifest->m_vPlugins) { for (auto const& pl : pManifest->m_vPlugins) {
std::string message = std::string{Colors::RESET} + " " + pl.name + " by "; std::string message = "" + pl.name + " by ";
for (auto& a : pl.authors) { for (auto const& a : pl.authors) {
message += a + ", "; message += a + ", ";
} }
if (pl.authors.size() > 0) { if (pl.authors.size() > 0) {
@ -220,19 +225,19 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
if (!pManifest->m_sRepository.commitPins.empty()) { if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins // check commit pins
progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking"); progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) { for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash) if (hl != HLVER.hash)
continue; continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting"); progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin); execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init"); ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "git submodule update --init returned: " << ret << "\n"; std::println("{}", verboseString("git submodule update --init returned: {}", ret));
break; break;
} }
@ -244,12 +249,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HEADERSSTATUS = headersValid(); const auto HEADERSSTATUS = headersValid();
if (HEADERSSTATUS != HEADERS_OK) { if (HEADERSSTATUS != HEADERS_OK) {
std::cerr << "\n" << headerError(HEADERSSTATUS); std::println("\n{}", headerError(HEADERSSTATUS));
return false; return false;
} }
progress.m_iSteps = 3; progress.m_iSteps = 3;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " Hyprland headers OK"); progress.printMessageAbove(successString("Hyprland headers OK"));
progress.m_szCurrentMessage = "Building plugin(s)"; progress.m_szCurrentMessage = "Building plugin(s)";
progress.print(); progress.print();
@ -257,35 +262,36 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::string out; std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) { if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Not building " + p.name + ": your Hyprland version is too old.\n"); progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
p.failed = true; p.failed = true;
continue; continue;
} }
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); progress.printMessageAbove(infoString("Building {}", p.name));
for (auto& bs : p.buildSteps) { for (auto const& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs); const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
} }
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n"; std::println("{}", verboseString("shell returned: " + out));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) { if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " failed to build.\n" + progress.printMessageAbove(failureString("Plugin {} failed to build.\n"
" This likely means that the plugin is either outdated, not yet available for your version, or broken.\n If you are on -git, update " " This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
"first.\n Try re-running with -v to see " " If you are on -git, update first\n"
"more verbose output.\n"); " Try re-running with -v to see more verbose output.\n",
p.name));
p.failed = true; p.failed = true;
continue; continue;
} }
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output); progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
} }
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " all plugins built"); progress.printMessageAbove(successString("all plugins built"));
progress.m_iSteps = 4; progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing repository"; progress.m_szCurrentMessage = "Installing repository";
progress.print(); progress.print();
@ -299,18 +305,18 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
repo.url = url; repo.url = url;
repo.rev = rev; repo.rev = rev;
repo.hash = repohash; repo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) { for (auto const& p : pManifest->m_vPlugins) {
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed}); repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
} }
DataState::addNewPluginRepo(repo); DataState::addNewPluginRepo(repo);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed repository"); progress.printMessageAbove(successString("installed repository"));
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " you can now enable the plugin(s) with hyprpm enable"); progress.printMessageAbove(successString("you can now enable the plugin(s) with hyprpm enable"));
progress.m_iSteps = 5; progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
std::cout << "\n"; std::print("\n");
// remove build files // remove build files
std::filesystem::remove_all(m_szWorkingPluginDirectory); std::filesystem::remove_all(m_szWorkingPluginDirectory);
@ -320,7 +326,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
bool CPluginManager::removePluginRepo(const std::string& urlOrName) { bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
if (!DataState::pluginRepoExists(urlOrName)) { if (!DataState::pluginRepoExists(urlOrName)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not remove the repository. Repository is not installed.\n"; std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
return false; return false;
} }
@ -331,7 +337,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
std::getline(std::cin, input); std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') { if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n"; std::println("Aborting.");
return false; return false;
} }
@ -347,7 +353,7 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_MISSING; return HEADERS_MISSING;
// find headers commit // find headers commit
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath()); const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str()); auto headers = execAndGet(cmd.c_str());
if (!headers.contains("-I/")) if (!headers.contains("-I/"))
@ -355,7 +361,7 @@ eHeadersErrors CPluginManager::headersValid() {
headers.pop_back(); // pop newline headers.pop_back(); // pop newline
std::string verHeader = ""; std::string verHeader;
while (!headers.empty()) { while (!headers.empty()) {
const auto PATH = headers.substr(0, headers.find(" -I/", 3)); const auto PATH = headers.substr(0, headers.find(" -I/", 3));
@ -406,7 +412,7 @@ bool CPluginManager::updateHeaders(bool force) {
const auto HLVER = getHyprlandVersion(); const auto HLVER = getHyprlandVersion();
if (!hasDeps()) { if (!hasDeps()) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio\n"; std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio"));
return false; return false;
} }
@ -416,7 +422,7 @@ bool CPluginManager::updateHeaders(bool force) {
} }
if (!force && headersValid() == HEADERS_OK) { if (!force && headersValid() == HEADERS_OK) {
std::cout << "\n" << std::string{Colors::GREEN} + "" + Colors::RESET + " Headers up to date.\n"; std::println("\n{}", successString("Headers up to date."));
return true; return true;
} }
@ -430,74 +436,73 @@ bool CPluginManager::updateHeaders(bool force) {
const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME; const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME;
if (!createSafeDirectory(WORKINGDIR)) { if (!createSafeDirectory(WORKINGDIR)) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not prepare working dir for hl\n"; std::println("\n{}", failureString("Could not prepare working dir for hl"));
return false; return false;
} }
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " Cloning https://github.com/hyprwm/hyprland, this might take a moment."); progress.printMessageAbove(statusString("!", Colors::YELLOW, "Cloning https://github.com/hyprwm/Hyprland, this might take a moment."));
const bool bShallow = (HLVER.branch == "main" || HLVER.branch == "") && !m_bNoShallow; const bool bShallow = (HLVER.branch == "main") && !m_bNoShallow;
// let us give a bit of leg-room for shallowing // let us give a bit of leg-room for shallowing
// due to timezones, etc. // due to timezones, etc.
const std::string SHALLOW_DATE = const std::string SHALLOW_DATE = trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+%a %b %d %H:%M:%S %Y'");
trim(HLVER.date).empty() ? "" : execAndGet("LC_TIME=\"en_US.UTF-8\" date --date='" + HLVER.date + " - 1 weeks' '+\%a \%b \%d \%H:\%M:\%S \%Y'");
if (m_bVerbose && bShallow) if (m_bVerbose && bShallow)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will shallow since: " + SHALLOW_DATE); progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
std::string ret = std::string ret =
execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")); execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-" + USERNAME + (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""));
if (!std::filesystem::exists(WORKINGDIR)) { if (!std::filesystem::exists(WORKINGDIR)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Clone failed. Retrying without shallow."); progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME); ret = execAndGet("cd /tmp/hyprpm && git clone --recursive https://github.com/hyprwm/hyprland hyprland-" + USERNAME);
} }
if (!std::filesystem::exists(WORKINGDIR + "/.git")) { if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not clone the hyprland repository. shell returned:\n" << ret << "\n"; std::println(stderr, "\n{}", failureString("Could not clone the Hyprland repository. shell returned:\n{}", ret));
return false; return false;
} }
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned"); progress.printMessageAbove(successString("Hyprland cloned"));
progress.m_iSteps = 2; progress.m_iSteps = 2;
progress.m_szCurrentMessage = "Checking out sources"; progress.m_szCurrentMessage = "Checking out sources";
progress.print(); progress.print();
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "will run: " + "cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1"); progress.printMessageAbove(verboseString("will run: cd {} && git checkout {} 2>&1", WORKINGDIR, HLVER.hash));
ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1"); ret = execAndGet("cd " + WORKINGDIR + " && git checkout " + HLVER.hash + " 2>&1");
if (ret.contains("fatal: unable to read tree")) { if (ret.contains("fatal: unable to read tree")) {
std::cerr << "\n" std::println(stderr, "\n{}",
<< Colors::RED << "" << Colors::RESET failureString("Could not checkout the running Hyprland commit. If you are on -git, try updating.\n"
<< " Could not checkout the running Hyprland commit. If you are on -git, try updating.\nYou can also try re-running hyprpm update with --no-shallow.\n"; "You can also try re-running hyprpm update with --no-shallow."));
return false; return false;
} }
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (co): " + ret); progress.printMessageAbove(verboseString("git returned (co): {}", ret));
ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash); ret = execAndGet("cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER.hash);
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "git returned (rs): " + ret); progress.printMessageAbove(verboseString("git returned (rs): {}", ret));
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " checked out to running ver"); progress.printMessageAbove(successString("checked out to running ver"));
progress.m_iSteps = 3; progress.m_iSteps = 3;
progress.m_szCurrentMessage = "Building Hyprland"; progress.m_szCurrentMessage = "Building Hyprland";
progress.print(); progress.print();
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland"); progress.printMessageAbove(statusString("!", Colors::YELLOW, "configuring Hyprland"));
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "setting PREFIX for cmake to " + DataState::getHeadersPath()); progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR, ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR,
DataState::getHeadersPath())); DataState::getHeadersPath()));
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret); progress.printMessageAbove(verboseString("cmake returned: {}", ret));
if (ret.contains("CMake Error at")) { if (ret.contains("CMake Error at")) {
// missing deps, let the user know. // missing deps, let the user know.
@ -506,48 +511,46 @@ bool CPluginManager::updateHeaders(bool force) {
missing = missing.substr(0, missing.find("-- Configuring incomplete")); missing = missing.substr(0, missing.find("-- Configuring incomplete"));
missing = missing.substr(0, missing.find_last_of('\n')); missing = missing.substr(0, missing.find_last_of('\n'));
std::cerr << "\n" std::println(stderr, "\n{}",
<< Colors::RED << "" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n" failureString("Could not configure the hyprland source, cmake complained:\n{}\n\n"
<< missing << "\n\nThis likely means that you are missing the above dependencies or they are out of date.\n"; "This likely means that you are missing the above dependencies or they are out of date.",
missing));
return false; return false;
} }
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland"); progress.printMessageAbove(successString("configured Hyprland"));
progress.m_iSteps = 4; progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources"; progress.m_szCurrentMessage = "Installing sources";
progress.print(); progress.print();
std::string cmd = const std::string& cmd =
std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR); std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
if (m_bVerbose) if (m_bVerbose)
progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "installation will run: " + cmd); progress.printMessageAbove(verboseString("installation will run: {}", cmd));
ret = execAndGet(cmd); ret = execAndGet(cmd);
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n"; std::println("{}", verboseString("installer returned: {}", ret));
// remove build files // remove build files
std::filesystem::remove_all(WORKINGDIR); std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid(); auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) { if (HEADERSVALID == HEADERS_OK) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed headers"); progress.printMessageAbove(successString("installed headers"));
progress.m_iSteps = 5; progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
std::cout << "\n"; std::print("\n");
} else { } else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID) + " (" + progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
headerErrorShort(HEADERSVALID) + ")");
progress.m_iSteps = 5; progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed"; progress.m_szCurrentMessage = "Failed";
progress.print(); progress.print();
std::cout << "\n"; std::print(stderr, "\n\n{}", headerError(HEADERSVALID));
std::cerr << "\n" << headerError(HEADERSVALID);
return false; return false;
} }
@ -557,14 +560,14 @@ bool CPluginManager::updateHeaders(bool force) {
bool CPluginManager::updatePlugins(bool forceUpdateAll) { bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (headersValid() != HEADERS_OK) { if (headersValid() != HEADERS_OK) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n"; std::println("{}", failureString("headers are not up-to-date, please run hyprpm update."));
return false; return false;
} }
const auto REPOS = DataState::getAllRepositories(); const auto REPOS = DataState::getAllRepositories();
if (REPOS.size() < 1) { if (REPOS.size() < 1) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " No repos to update.\n"; std::println("{}", failureString("No repos to update."));
return true; return true;
} }
@ -579,32 +582,33 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
const std::string USERNAME = getpwuid(getuid())->pw_name; const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME; m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
for (auto& repo : REPOS) { for (auto const& repo : REPOS) {
bool update = forceUpdateAll; bool update = forceUpdateAll;
progress.m_iSteps++; progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating " + repo.name; progress.m_szCurrentMessage = "Updating " + repo.name;
progress.print(); progress.print();
progress.printMessageAbove(std::string{Colors::RESET} + " → checking for updates for " + repo.name); progress.printMessageAbove(infoString("checking for updates for {}", repo.name));
createSafeDirectory(m_szWorkingPluginDirectory); createSafeDirectory(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::RESET} + " → Cloning " + repo.url); progress.printMessageAbove(infoString("Cloning {}", repo.url));
std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " " + USERNAME); std::string ret = execAndGet("cd /tmp/hyprpm && git clone --recursive " + repo.url + " " + USERNAME);
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not clone repo: shell returned:\n" + ret; std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
return false; return false;
} }
if (!repo.rev.empty()) { if (!repo.rev.empty()) {
progress.printMessageAbove(std::string{Colors::RESET} + " → Plugin has revision set, resetting: " + repo.rev); progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev); std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
if (ret.compare(0, 6, "fatal:") == 0) { if (ret.compare(0, 6, "fatal:") == 0) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " could not check out revision " + repo.rev + ": shell returned:\n" + ret; std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
return false; return false;
} }
} }
@ -620,7 +624,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!update) { if (!update) {
std::filesystem::remove_all(m_szWorkingPluginDirectory); std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " is up-to-date."); progress.printMessageAbove(successString("repository {} is up-to-date.", repo.name));
progress.m_iSteps++; progress.m_iSteps++;
progress.print(); progress.print();
continue; continue;
@ -628,41 +632,41 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
// we need to update // we need to update
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " has updates."); progress.printMessageAbove(successString("repository {} has updates.", repo.name));
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + repo.name); progress.printMessageAbove(infoString("Building {}", repo.name));
progress.m_iSteps++; progress.m_iSteps++;
progress.print(); progress.print();
std::unique_ptr<CManifest> pManifest; std::unique_ptr<CManifest> pManifest;
if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) { if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprpm.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprpm manifest"); progress.printMessageAbove(successString("found hyprpm manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml"); pManifest = std::make_unique<CManifest>(MANIFEST_HYPRPM, m_szWorkingPluginDirectory + "/hyprpm.toml");
} else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) { } else if (std::filesystem::exists(m_szWorkingPluginDirectory + "/hyprload.toml")) {
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " found hyprload manifest"); progress.printMessageAbove(successString("found hyprload manifest"));
pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml"); pManifest = std::make_unique<CManifest>(MANIFEST_HYPRLOAD, m_szWorkingPluginDirectory + "/hyprload.toml");
} }
if (!pManifest) { if (!pManifest) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository does not have a valid manifest\n"; std::println(stderr, "\n{}", failureString("The provided plugin repository does not have a valid manifest"));
continue; continue;
} }
if (!pManifest->m_bGood) { if (!pManifest->m_bGood) {
std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " The provided plugin repository has a corrupted manifest\n"; std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
continue; continue;
} }
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) { if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
// check commit pins unless a revision is specified // check commit pins unless a revision is specified
progress.printMessageAbove(std::string{Colors::RESET} + " → Manifest has " + std::to_string(pManifest->m_sRepository.commitPins.size()) + " pins, checking"); progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
for (auto& [hl, plugin] : pManifest->m_sRepository.commitPins) { for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash) if (hl != HLVER.hash)
continue; continue;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting"); progress.printMessageAbove(successString("commit pin {} matched hl, resetting", plugin));
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin); execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin);
} }
@ -672,32 +676,33 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
std::string out; std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) { if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Not building " + p.name + ": your Hyprland version is too old.\n"); progress.printMessageAbove(failureString("Not building {}: your Hyprland version is too old.\n", p.name));
p.failed = true; p.failed = true;
continue; continue;
} }
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name); progress.printMessageAbove(infoString("Building {}", p.name));
for (auto& bs : p.buildSteps) { for (auto const& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs); const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
} }
if (m_bVerbose) if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "shell returned: " << out << "\n"; std::println("{}", verboseString("shell returned: {}", out));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) { if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/" + p.output)) {
std::cerr << "\n" std::println(stderr,
<< Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " failed to build.\n" "\n{}\n"
<< " This likely means that the plugin is either outdated, not yet available for your version, or broken.\n If you are on -git, update first.\n Try " " This likely means that the plugin is either outdated, not yet available for your version, or broken.\n"
"re-running with -v to see more verbose " "If you are on -git, update first.\n"
"output.\n"; "Try re-running with -v to see more verbose output.",
failureString("Plugin {} failed to build.", p.name));
p.failed = true; p.failed = true;
continue; continue;
} }
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " built " + p.name + " into " + p.output); progress.printMessageAbove(successString("built {} into {}", p.name, p.output));
} }
// add repo toml to DataState // add repo toml to DataState
@ -709,7 +714,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (repohash.length() > 0) if (repohash.length() > 0)
repohash.pop_back(); repohash.pop_back();
newrepo.hash = repohash; newrepo.hash = repohash;
for (auto& p : pManifest->m_vPlugins) { for (auto const& p : pManifest->m_vPlugins) {
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; }); 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, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false}); newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
} }
@ -718,7 +723,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
std::filesystem::remove_all(m_szWorkingPluginDirectory); std::filesystem::remove_all(m_szWorkingPluginDirectory);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name); progress.printMessageAbove(successString("updated {}", repo.name));
} }
progress.m_iSteps++; progress.m_iSteps++;
@ -733,7 +738,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Done!"; progress.m_szCurrentMessage = "Done!";
progress.print(); progress.print();
std::cout << "\n"; std::print("\n");
return true; return true;
} }
@ -741,27 +746,27 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
bool CPluginManager::enablePlugin(const std::string& name) { bool CPluginManager::enablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, true); bool ret = DataState::setPluginEnabled(name, true);
if (ret) if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Enabled " << name << "\n"; std::println("{}", successString("Enabled {}", name));
return ret; return ret;
} }
bool CPluginManager::disablePlugin(const std::string& name) { bool CPluginManager::disablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, false); bool ret = DataState::setPluginEnabled(name, false);
if (ret) if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Disabled " << name << "\n"; std::println("{}", successString("Disabled {}", name));
return ret; return ret;
} }
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() { ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (headersValid() != HEADERS_OK) { if (headersValid() != HEADERS_OK) {
std::cerr << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n"; std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
return LOADSTATE_HEADERS_OUTDATED; return LOADSTATE_HEADERS_OUTDATED;
} }
const auto HOME = getenv("HOME"); const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) { if (!HOME || !HIS) {
std::cerr << "PluginManager: no $HOME or HIS\n"; std::println(stderr, "PluginManager: no $HOME or HIS");
return LOADSTATE_FAIL; return LOADSTATE_FAIL;
} }
const auto HYPRPMPATH = DataState::getDataStatePath() + "/"; const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
@ -770,14 +775,14 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
std::vector<std::string> loadedPlugins; std::vector<std::string> loadedPlugins;
std::cout << Colors::GREEN << "" << Colors::RESET << " Ensuring plugin load state\n"; std::println("{}", successString("Ensuring plugin load state"));
// iterate line by line // iterate line by line
while (!pluginLines.empty()) { while (!pluginLines.empty()) {
auto plLine = pluginLines.substr(0, pluginLines.find("\n")); auto plLine = pluginLines.substr(0, pluginLines.find('\n'));
if (pluginLines.find("\n") != std::string::npos) if (pluginLines.find('\n') != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find("\n") + 1); pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
else else
pluginLines = ""; pluginLines = "";
@ -794,8 +799,8 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
const auto REPOS = DataState::getAllRepositories(); const auto REPOS = DataState::getAllRepositories();
auto enabled = [REPOS](const std::string& plugin) -> bool { auto enabled = [REPOS](const std::string& plugin) -> bool {
for (auto& r : REPOS) { for (auto const& r : REPOS) {
for (auto& p : r.plugins) { for (auto const& p : r.plugins) {
if (p.name == plugin && p.enabled) if (p.name == plugin && p.enabled)
return true; return true;
} }
@ -805,8 +810,8 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
}; };
auto repoForName = [REPOS](const std::string& name) -> std::string { auto repoForName = [REPOS](const std::string& name) -> std::string {
for (auto& r : REPOS) { for (auto const& r : REPOS) {
for (auto& p : r.plugins) { for (auto const& p : r.plugins) {
if (p.name == name) if (p.name == name)
return r.name; return r.name;
} }
@ -816,17 +821,17 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
}; };
// unload disabled plugins // unload disabled plugins
for (auto& p : loadedPlugins) { for (auto const& p : loadedPlugins) {
if (!enabled(p)) { if (!enabled(p)) {
// unload // unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false); loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::cout << Colors::GREEN << "" << Colors::RESET << " Unloaded " << p << "\n"; std::println("{}", successString("Unloaded {}", p));
} }
} }
// load enabled plugins // load enabled plugins
for (auto& r : REPOS) { for (auto const& r : REPOS) {
for (auto& p : r.plugins) { for (auto const& p : r.plugins) {
if (!p.enabled) if (!p.enabled)
continue; continue;
@ -834,11 +839,11 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
continue; continue;
loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true); loadUnloadPlugin(HYPRPMPATH + repoForName(p.name) + "/" + p.filename, true);
std::cout << Colors::GREEN << "" << Colors::RESET << " Loaded " << p.name << "\n"; std::println("{}", successString("Loaded {}", p.name));
} }
} }
std::cout << Colors::GREEN << "" << Colors::RESET << " Plugin load state ensured\n"; std::println("{}", successString("Plugin load state ensured"));
return LOADSTATE_OK; return LOADSTATE_OK;
} }
@ -855,17 +860,18 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
void CPluginManager::listAllPlugins() { void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories(); const auto REPOS = DataState::getAllRepositories();
for (auto& r : REPOS) { for (auto const& r : REPOS) {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n"; std::println("{}", infoString("Repository {}:", r.name));
for (auto& p : r.plugins) { for (auto const& p : r.plugins) {
std::println(" │ Plugin {}", p.name);
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name;
if (!p.failed) if (!p.failed)
std::cout << "\n └─ enabled: " << (p.enabled ? Colors::GREEN : Colors::RED) << (p.enabled ? "true" : "false") << Colors::RESET << "\n"; std::println(" └─ enabled: {}", (p.enabled ? std::string{Colors::GREEN} + "true" : std::string{Colors::RED} + "false"));
else else
std::cout << "\n └─ enabled: " << Colors::RED << "Plugin failed to build" << Colors::RESET << "\n"; std::println(" └─ enabled: {}Plugin failed to build", Colors::RED);
std::println("{}", Colors::RESET);
} }
} }
} }
@ -876,19 +882,19 @@ void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int duratio
std::string CPluginManager::headerError(const eHeadersErrors err) { std::string CPluginManager::headerError(const eHeadersErrors err) {
switch (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_CORRUPTED: return failureString("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_MISMATCHED: return failureString("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_NOT_HYPRLAND: return failureString("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_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
case HEADERS_DUPLICATED: { case HEADERS_DUPLICATED: {
return std::string{Colors::RED} + "" + Colors::RESET + " Headers duplicated!!! This is a very bad sign.\n" + return failureString("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" + "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"; "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; default: break;
} }
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n"; return failureString("Unknown header error. Please run hyprpm update to fix those.\n");
} }
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) { std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
@ -904,9 +910,9 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
} }
bool CPluginManager::hasDeps() { bool CPluginManager::hasDeps() {
std::vector<std::string> deps = {"meson", "cpio", "cmake"}; std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config"};
for (auto& d : deps) { for (auto const& d : deps) {
if (!execAndGet("which " + d + " 2>&1").contains("/")) if (!execAndGet("command -v " + d).contains("/"))
return false; return false;
} }

View file

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
enum eHeadersErrors { enum eHeadersErrors {
HEADERS_OK = 0, HEADERS_OK = 0,
@ -68,7 +69,7 @@ class CPluginManager {
std::string headerError(const eHeadersErrors err); std::string headerError(const eHeadersErrors err);
std::string headerErrorShort(const eHeadersErrors err); std::string headerErrorShort(const eHeadersErrors err);
std::string m_szWorkingPluginDirectory = ""; std::string m_szWorkingPluginDirectory;
}; };
inline std::unique_ptr<CPluginManager> g_pPluginManager; inline std::unique_ptr<CPluginManager> g_pPluginManager;

View file

@ -0,0 +1,32 @@
#pragma once
#include <format>
#include <string>
#include "Colors.hpp"
template <typename... Args>
std::string statusString(const std::string_view emoji, const std::string_view color, const std::string_view fmt, Args&&... args) {
std::string ret = std::format("{}{}{} ", color, emoji, Colors::RESET);
ret += std::vformat(fmt, std::make_format_args(args...));
return ret;
}
template <typename... Args>
std::string successString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::GREEN, fmt, args...);
}
template <typename... Args>
std::string failureString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::RED, fmt, args...);
}
template <typename... Args>
std::string verboseString(const std::string_view fmt, Args&&... args) {
return statusString("[v]", Colors::BLUE, fmt, args...);
}
template <typename... Args>
std::string infoString(const std::string_view fmt, Args&&... args) {
return statusString("", Colors::RESET, fmt, args...);
}

View file

@ -1,15 +1,16 @@
#include "progress/CProgressBar.hpp"
#include "helpers/Colors.hpp" #include "helpers/Colors.hpp"
#include "helpers/StringUtils.hpp"
#include "core/PluginManager.hpp" #include "core/PluginManager.hpp"
#include "core/DataState.hpp" #include "core/DataState.hpp"
#include <iostream> #include <cstdio>
#include <vector> #include <vector>
#include <string> #include <string>
#include <print>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
add [url] [git rev] Install a new plugin repository from git. Git revision add [url] [git rev] Install a new plugin repository from git. Git revision
is optional, when set, commit locks are ignored. is optional, when set, commit locks are ignored.
@ -37,7 +38,7 @@ int main(int argc, char** argv, char** envp) {
} }
if (ARGS.size() < 2) { if (ARGS.size() < 2) {
std::cout << HELP; std::println(stderr, "{}", HELP);
return 1; return 1;
} }
@ -47,7 +48,7 @@ int main(int argc, char** argv, char** envp) {
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) { if (ARGS[i].starts_with("-")) {
if (ARGS[i] == "--help" || ARGS[i] == "-h") { if (ARGS[i] == "--help" || ARGS[i] == "-h") {
std::cout << HELP; std::println("{}", HELP);
return 0; return 0;
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") { } else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true; notify = true;
@ -57,9 +58,9 @@ int main(int argc, char** argv, char** envp) {
noShallow = true; noShallow = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") { } else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true; force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n"; std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
} else { } else {
std::cerr << "Unrecognized option " << ARGS[i] << "\n"; std::println(stderr, "Unrecognized option {}", ARGS[i]);
return 1; return 1;
} }
} else { } else {
@ -68,7 +69,7 @@ int main(int argc, char** argv, char** envp) {
} }
if (command.empty()) { if (command.empty()) {
std::cout << HELP; std::println(stderr, "{}", HELP);
return 0; return 0;
} }
@ -78,7 +79,7 @@ int main(int argc, char** argv, char** envp) {
if (command[0] == "add") { if (command[0] == "add") {
if (command.size() < 2) { if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for add.\n"; std::println(stderr, "{}", failureString("Not enough args for add."));
return 1; return 1;
} }
@ -90,7 +91,7 @@ int main(int argc, char** argv, char** envp) {
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1; return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
} else if (command[0] == "remove") { } else if (command[0] == "remove") {
if (ARGS.size() < 2) { if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for remove.\n"; std::println(stderr, "{}", failureString("Not enough args for remove."));
return 1; return 1;
} }
@ -116,12 +117,12 @@ int main(int argc, char** argv, char** envp) {
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers"); g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
} else if (command[0] == "enable") { } else if (command[0] == "enable") {
if (ARGS.size() < 2) { if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for enable.\n"; std::println(stderr, "{}", failureString("Not enough args for enable."));
return 1; return 1;
} }
if (!g_pPluginManager->enablePlugin(command[1])) { if (!g_pPluginManager->enablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't enable plugin (missing?)\n"; std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
return 1; return 1;
} }
@ -130,12 +131,12 @@ int main(int argc, char** argv, char** envp) {
return 1; return 1;
} else if (command[0] == "disable") { } else if (command[0] == "disable") {
if (command.size() < 2) { if (command.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for disable.\n"; std::println(stderr, "{}", failureString("Not enough args for disable."));
return 1; return 1;
} }
if (!g_pPluginManager->disablePlugin(command[1])) { if (!g_pPluginManager->disablePlugin(command[1])) {
std::cerr << Colors::RED << "" << Colors::RESET << " Couldn't disable plugin (missing?)\n"; std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
return 1; return 1;
} }
@ -160,7 +161,7 @@ int main(int argc, char** argv, char** envp) {
} else if (command[0] == "list") { } else if (command[0] == "list") {
g_pPluginManager->listAllPlugins(); g_pPluginManager->listAllPlugins();
} else { } else {
std::cout << HELP; std::println(stderr, "{}", HELP);
return 1; return 1;
} }

View file

@ -1,15 +1,31 @@
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true) globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n') src = globber.stdout().strip().split('\n')
executable('hyprpm', src, executable(
'hyprpm',
src,
dependencies: [ dependencies: [
dependency('hyprutils', version: '>= 0.1.1'), dependency('hyprutils', version: '>= 0.1.1'),
dependency('threads'), dependency('threads'),
dependency('tomlplusplus') dependency('tomlplusplus'),
], ],
install : true install: true,
) )
install_data('../hyprpm.bash', install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), install_tag: 'runtime', rename: 'hyprpm') install_data(
install_data('../hyprpm.fish', install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), install_tag: 'runtime') '../hyprpm.bash',
install_data('../hyprpm.zsh', install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), install_tag: 'runtime', rename: '_hyprpm') install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
install_tag: 'runtime',
rename: 'hyprpm',
)
install_data(
'../hyprpm.fish',
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
install_tag: 'runtime',
)
install_data(
'../hyprpm.zsh',
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
install_tag: 'runtime',
rename: '_hyprpm',
)

View file

@ -1,11 +1,11 @@
#include "CProgressBar.hpp" #include "CProgressBar.hpp"
#include <iostream> #include <sys/ioctl.h>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <format> #include <format>
#include <sys/ioctl.h> #include <print>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -16,11 +16,12 @@ void CProgressBar::printMessageAbove(const std::string& msg) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
std::string spaces; std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) { for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' '; spaces += ' ';
} }
std::cout << "\r" << spaces << "\r" << msg << "\n"; std::println("\r{}\r{}", spaces, msg);
print(); print();
} }
@ -29,15 +30,16 @@ void CProgressBar::print() {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (m_bFirstPrint) if (m_bFirstPrint)
std::cout << "\n"; std::print("\n");
m_bFirstPrint = false; m_bFirstPrint = false;
std::string spaces; std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) { for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' '; spaces += ' ';
} }
std::cout << "\r" << spaces << "\r"; std::print("\r{}\r", spaces);
std::string message = ""; std::string message = "";
@ -74,7 +76,7 @@ void CProgressBar::print() {
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " "; message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
// draw message // draw message
std::cout << message + " " + m_szCurrentMessage; std::print("{} {}", message, m_szCurrentMessage);
std::fflush(stdout); std::fflush(stdout);
} }

View file

@ -1,13 +1,17 @@
project('Hyprland', 'cpp', 'c', project(
version : run_command('cat', join_paths(meson.source_root(), 'VERSION'), check: true).stdout().strip(), 'Hyprland',
'cpp',
'c',
version: run_command('cat', join_paths(meson.project_source_root(), 'VERSION'), check: true).stdout().strip(),
default_options: [ default_options: [
'warning_level=2', 'warning_level=2',
'default_library=static', 'default_library=static',
'optimization=3', 'optimization=3',
'buildtype=release', 'buildtype=release',
'debug=false', 'debug=false',
'cpp_std=c++23', 'cpp_std=c++26',
]) ],
)
datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"' datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"'
add_project_arguments( add_project_arguments(
@ -16,16 +20,20 @@ add_project_arguments(
'-Wno-unused-value', '-Wno-unused-value',
'-Wno-missing-field-initializers', '-Wno-missing-field-initializers',
'-Wno-narrowing', '-Wno-narrowing',
'-Wno-pointer-arith', '-Wno-pointer-arith', datarootdir,
datarootdir, '-DHYPRLAND_VERSION="' + meson.project_version() + '"',
], ],
language: 'cpp') language: 'cpp',
)
cpp_compiler = meson.get_compiler('cpp') cpp_compiler = meson.get_compiler('cpp')
if cpp_compiler.check_header('execinfo.h') if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp') add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif endif
aquamarine = dependency('aquamarine', version: '>=0.4.2')
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
xcb_dep = dependency('xcb', required: get_option('xwayland')) xcb_dep = dependency('xcb', required: get_option('xwayland'))
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland')) xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland')) xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland'))
@ -34,9 +42,7 @@ xcb_render_dep = dependency('xcb-render', required: get_option('xwayland'))
xcb_res_dep = dependency('xcb-res', required: get_option('xwayland')) xcb_res_dep = dependency('xcb-res', required: get_option('xwayland'))
xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland')) xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland'))
cmake = import('cmake') gio_dep = dependency('gio-2.0', required: true)
udis = cmake.subproject('udis86')
udis86 = udis.dependency('libudis86')
if not xcb_dep.found() if not xcb_dep.found()
add_project_arguments('-DNO_XWAYLAND', language: 'cpp') add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
@ -45,6 +51,7 @@ endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false) backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
# Handle options
if get_option('systemd').enabled() if get_option('systemd').enabled()
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp') add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
endif endif
@ -57,14 +64,22 @@ 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', check: true) # Generate hyprland version and populate version.h
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
# Install headers
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
install_headers(file, subdir: 'hyprland', preserve_path: true) install_headers(file, subdir: 'hyprland', preserve_path: true)
endforeach endforeach
tracy = dependency('tracy', static: true, required: get_option('tracy_enable'))
if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized'
warning('Profiling builds should set -- buildtype = debugoptimized')
endif
subdir('protocols') subdir('protocols')
subdir('src') subdir('src')
subdir('hyprctl') subdir('hyprctl')
@ -73,6 +88,7 @@ subdir('assets')
subdir('example') subdir('example')
subdir('docs') subdir('docs')
# Generate hyprland.pc
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig') pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
import('pkgconfig').generate( import('pkgconfig').generate(

View file

@ -1,3 +1,4 @@
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration') option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer') option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')

View file

@ -1,43 +1,33 @@
{ {
lib, lib,
stdenv, stdenv,
stdenvAdapters,
pkg-config, pkg-config,
pkgconf, pkgconf,
makeWrapper, makeWrapper,
cmake, meson,
ninja, ninja,
aquamarine, aquamarine,
binutils, binutils,
cairo, cairo,
expat,
fribidi,
git, git,
hwdata,
hyprcursor, hyprcursor,
hyprland-protocols,
hyprlang, hyprlang,
hyprutils, hyprutils,
hyprwayland-scanner, hyprwayland-scanner,
jq,
libGL, libGL,
libdatrie,
libdisplay-info,
libdrm, libdrm,
libexecinfo, libexecinfo,
libinput, libinput,
libliftoff,
libselinux,
libsepol,
libthai,
libuuid,
libxkbcommon, libxkbcommon,
libuuid,
mesa, mesa,
pango, pango,
pciutils, pciutils,
pcre2,
python3,
seatd,
systemd, systemd,
tomlplusplus, tomlplusplus,
udis86-hyprland,
wayland, wayland,
wayland-protocols, wayland-protocols,
wayland-scanner, wayland-scanner,
@ -50,25 +40,39 @@
wrapRuntimeDeps ? true, wrapRuntimeDeps ? true,
version ? "git", version ? "git",
commit, commit,
revCount,
date, date,
# deprecated flags # deprecated flags
enableNvidiaPatches ? false, enableNvidiaPatches ? false,
nvidiaPatches ? false, nvidiaPatches ? false,
hidpiXWayland ? false, hidpiXWayland ? false,
}: }: let
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed."; inherit (builtins) baseNameOf foldl';
assert lib.assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed."; inherit (lib.asserts) assertMsg;
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland"; inherit (lib.attrsets) mapAttrsToList;
stdenv.mkDerivation { inherit (lib.lists) flatten concatLists optional optionals;
pname = "hyprland${lib.optionalString debug "-debug"}"; inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable;
adapters = flatten [
stdenvAdapters.useMoldLinker
];
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
in
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
customStdenv.mkDerivation {
pname = "hyprland${optionalString debug "-debug"}";
inherit version; inherit version;
src = lib.cleanSourceWith { src = cleanSourceWith {
filter = name: type: let filter = name: _type: let
baseName = baseNameOf (toString name); baseName = baseNameOf (toString name);
in in
! (lib.hasSuffix ".nix" baseName); ! (hasSuffix ".nix" baseName);
src = lib.cleanSource ../.; src = cleanSource ../.;
}; };
postPatch = '' postPatch = ''
@ -79,20 +83,21 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
sed -i "s#@PREFIX@/##g" hyprland.pc.in sed -i "s#@PREFIX@/##g" hyprland.pc.in
''; '';
COMMITS = commit; COMMITS = revCount;
DATE = date; DATE = date;
DIRTY = lib.optionalString (commit == "") "dirty"; DIRTY = optionalString (commit == "") "dirty";
HASH = commit; HASH = commit;
depsBuildBuild = [
pkg-config
];
nativeBuildInputs = [ nativeBuildInputs = [
hyprwayland-scanner hyprwayland-scanner
jq
makeWrapper makeWrapper
cmake meson
ninja ninja
pkg-config pkg-config
python3 # for udis86
wayland-scanner
]; ];
outputs = [ outputs = [
@ -101,69 +106,66 @@ assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been remov
"dev" "dev"
]; ];
buildInputs = lib.concatLists [ buildInputs = concatLists [
[ [
aquamarine aquamarine
cairo cairo
expat
fribidi
git git
hwdata
hyprcursor hyprcursor
hyprland-protocols
hyprlang hyprlang
hyprutils hyprutils
libdatrie
libdisplay-info
libdrm libdrm
libGL libGL
libinput libinput
libliftoff
libselinux
libsepol
libthai
libuuid libuuid
libxkbcommon libxkbcommon
mesa mesa
pango pango
pciutils pciutils
pcre2
seatd
tomlplusplus tomlplusplus
udis86-hyprland
wayland wayland
wayland-protocols wayland-protocols
] wayland-scanner
(lib.optionals stdenv.hostPlatform.isMusl [libexecinfo])
(lib.optionals enableXWayland [
xorg.libxcb
xorg.libXcursor xorg.libXcursor
]
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
(optionals enableXWayland [
xorg.libxcb
xorg.libXdmcp xorg.libXdmcp
xorg.xcbutil
xorg.xcbutilerrors xorg.xcbutilerrors
xorg.xcbutilrenderutil xorg.xcbutilrenderutil
xorg.xcbutilwm xorg.xcbutilwm
xwayland xwayland
]) ])
(lib.optionals withSystemd [systemd]) (optional withSystemd systemd)
]; ];
cmakeBuildType = mesonBuildType =
if debug if debug
then "Debug" then "debugoptimized"
else "RelWithDebInfo"; else "release";
# we want as much debug info as possible # we want as much debug info as possible
dontStrip = debug; dontStrip = debug;
cmakeFlags = [ mesonFlags = flatten [
(lib.cmakeBool "NO_XWAYLAND" (!enableXWayland)) (mapAttrsToList mesonEnable {
(lib.cmakeBool "LEGACY_RENDERER" legacyRenderer) "xwayland" = enableXWayland;
(lib.cmakeBool "NO_SYSTEMD" (!withSystemd)) "legacy_renderer" = legacyRenderer;
"systemd" = withSystemd;
})
(mapAttrsToList mesonBool {
"b_pch" = false;
"tracy_enable" = false;
})
]; ];
postInstall = '' postInstall = ''
${lib.optionalString wrapRuntimeDeps '' ${optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \ wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [ --suffix PATH : ${makeBinPath [
binutils binutils
pciutils pciutils
pkgconf pkgconf

64
nix/formatter.nix Normal file
View file

@ -0,0 +1,64 @@
{
writeShellApplication,
deadnix,
statix,
alejandra,
llvmPackages_19,
fd,
}:
writeShellApplication {
name = "hyprland-treewide-formatter";
runtimeInputs = [
deadnix
statix
alejandra
llvmPackages_19.clang-tools
fd
];
text = ''
# shellcheck disable=SC2148
# common excludes
excludes="subprojects"
nix_format() {
if [ "$*" = 0 ]; then
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
elif [ -d "$1" ]; then
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
else
statix fix -- "$1"
deadnix -e "$1"
alejandra "$1"
fi
}
cpp_format() {
if [ "$*" = 0 ] || [ "$1" = "." ]; then
fd '.*\.cpp' . -E "$excludes" | xargs clang-format --verbose -i
elif [ -d "$1" ]; then
fd '.*\.cpp' "$1" -E "$excludes" | xargs clang-format --verbose -i
else
clang-format --verbose -i "$1"
fi
}
for i in "$@"; do
case ''${i##*.} in
"nix")
nix_format "$i"
;;
"cpp")
cpp_format "$i"
;;
*)
nix_format "$i"
cpp_format "$i"
;;
esac
done
'';
}

View file

@ -22,18 +22,21 @@ in {
# Dependencies # Dependencies
inputs.aquamarine.overlays.default inputs.aquamarine.overlays.default
inputs.hyprcursor.overlays.default inputs.hyprcursor.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprlang.overlays.default inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default inputs.hyprwayland-scanner.overlays.default
self.overlays.udis86
# Hyprland packages themselves # Hyprland packages themselves
(final: prev: let (final: _prev: let
date = mkDate (self.lastModifiedDate or "19700101"); date = mkDate (self.lastModifiedDate or "19700101");
in { in {
hyprland = final.callPackage ./default.nix { hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv; stdenv = final.gcc14Stdenv;
version = "${version}+date=${date}_${self.shortRev or "dirty"}"; version = "${version}+date=${date}_${self.shortRev or "dirty"}";
commit = self.rev or ""; commit = self.rev or "";
revCount = self.sourceInfo.revCount or "";
inherit date; inherit date;
}; };
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
@ -62,4 +65,20 @@ in {
hyprland-extras = lib.composeManyExtensions [ hyprland-extras = lib.composeManyExtensions [
inputs.xdph.overlays.xdg-desktop-portal-hyprland inputs.xdph.overlays.xdg-desktop-portal-hyprland
]; ];
# udis86 from nixpkgs is too old, and also does not provide a .pc file
# this version is the one used in the git submodule, and allows us to
# fetch the source without '?submodules=1'
udis86 = final: prev: {
udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: {
src = final.fetchFromGitHub {
owner = "canihavesomecoffee";
repo = "udis86";
rev = "5336633af70f3917760a6d441ff02d93477b0c86";
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
};
patches = [];
});
};
} }

View file

@ -1,76 +1,78 @@
wayland_protos = dependency('wayland-protocols', wayland_protos = dependency(
'wayland-protocols',
version: '>=1.32', version: '>=1.32',
fallback: 'wayland-protocols', fallback: 'wayland-protocols',
default_options: ['tests=false'], default_options: ['tests=false'],
) )
hyprland_protos = dependency('hyprland-protocols', hyprland_protos = dependency(
version: '>=0.2', 'hyprland-protocols',
version: '>=0.4',
fallback: 'hyprland-protocols', fallback: 'hyprland-protocols',
) )
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_protocol_dir = wayland_protos.get_variable('pkgdatadir')
hl_protocol_dir = hyprland_protos.get_variable('pkgdatadir') hyprland_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.8', native: true) hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.10', native: true)
hyprwayland_scanner = find_program( hyprwayland_scanner = find_program(
hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'), hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'),
native: true, native: true,
) )
new_protocols = [ protocols = [
['wlr-gamma-control-unstable-v1.xml'], 'wlr-gamma-control-unstable-v1.xml',
['wlr-foreign-toplevel-management-unstable-v1.xml'], 'wlr-foreign-toplevel-management-unstable-v1.xml',
['wlr-output-power-management-unstable-v1.xml'], 'wlr-output-power-management-unstable-v1.xml',
['input-method-unstable-v2.xml'], 'input-method-unstable-v2.xml',
['virtual-keyboard-unstable-v1.xml'], 'virtual-keyboard-unstable-v1.xml',
['wlr-virtual-pointer-unstable-v1.xml'], 'wlr-virtual-pointer-unstable-v1.xml',
['wlr-output-management-unstable-v1.xml'], 'wlr-output-management-unstable-v1.xml',
['kde-server-decoration.xml'], 'kde-server-decoration.xml',
['wlr-layer-shell-unstable-v1.xml'], 'wlr-layer-shell-unstable-v1.xml',
['wayland-drm.xml'], 'wayland-drm.xml',
['wlr-data-control-unstable-v1.xml'], 'wlr-data-control-unstable-v1.xml',
['wlr-screencopy-unstable-v1.xml'], 'wlr-screencopy-unstable-v1.xml',
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml'], hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'], hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
[hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'], hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'], wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], wayland_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
[wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'], wayland_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml',
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'], wayland_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
[wl_protocol_dir, 'staging/alpha-modifier/alpha-modifier-v1.xml'], wayland_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
[wl_protocol_dir, 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml'], wayland_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
[wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'], wayland_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'], wayland_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml',
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'], wayland_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml',
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml',
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'], wayland_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'], wayland_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'], wayland_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
[wl_protocol_dir, 'stable/tablet/tablet-v2.xml'], wayland_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], wayland_protocol_dir / 'stable/tablet/tablet-v2.xml',
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], wayland_protocol_dir / 'stable/presentation-time/presentation-time.xml',
[wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'], wayland_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
[wl_protocol_dir, 'staging/xwayland-shell/xwayland-shell-v1.xml'], wayland_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml',
[wl_protocol_dir, 'stable/viewporter/viewporter.xml'], wayland_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'], wayland_protocol_dir / 'stable/viewporter/viewporter.xml',
[wl_protocol_dir, 'staging/drm-lease/drm-lease-v1.xml'], wayland_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml',
[wl_protocol_dir, 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml'], wayland_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
] ]
wl_protos_src = [] wl_protocols = []
wl_protos_headers = [] foreach protocol : protocols
wl_protocols += custom_target(
new_wl_protos = [] protocol.underscorify(),
foreach p : new_protocols input: protocol,
xml = join_paths(p)
new_wl_protos += custom_target(
xml.underscorify(),
input: xml,
install: true, install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')], install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'], output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
@ -78,31 +80,26 @@ foreach p : new_protocols
) )
endforeach endforeach
wayland_server_dep = dependency('wayland-server', version: '>=1.20.0') # wayland.xml generation
wayland_server_dir = wayland_server_dep.get_variable('pkgdatadir') wayland_scanner = dependency('wayland-scanner')
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
wl_server_protos = [ wayland_xml = wayland_scanner_datadir / 'wayland.xml'
wayland_server_dir / 'wayland.xml' wayland_protocol = custom_target(
] wayland_xml.underscorify(),
wl_server_protos_gen = [] input: wayland_xml,
foreach p : wl_server_protos
wl_server_protos_gen += custom_target(
p.underscorify(),
input: p,
install: true, install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')], install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'], output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'], command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
) )
endforeach
lib_server_protos = static_library( lib_server_protos = static_library(
'server_protos', 'server_protos',
wl_protos_src + wl_protos_headers + new_wl_protos + wl_server_protos_gen, wl_protocols + wayland_protocol,
dependencies: wayland_server_dep.partial_dependency(compile_args: true),
) )
server_protos = declare_dependency( server_protos = declare_dependency(
link_with: lib_server_protos, link_with: lib_server_protos,
sources: wl_protos_headers + new_wl_protos, sources: wl_protocols + wayland_protocol,
) )

View file

@ -1,8 +1,8 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt
index f54cdf5d..ad7c3e73 100755 index f26a5c3c..3dfef333 100644
--- a/CMakeLists.txt --- a/CMakeLists.txt
+++ b/CMakeLists.txt +++ b/CMakeLists.txt
@@ -130,6 +130,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) @@ -143,6 +143,8 @@ 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)

File diff suppressed because it is too large Load diff

View file

@ -67,7 +67,7 @@ class CCompositor {
std::vector<PHLWINDOWREF> m_vWindowsFadingOut; std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
std::vector<PHLLSREF> m_vSurfacesFadingOut; std::vector<PHLLSREF> m_vSurfacesFadingOut;
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap; std::unordered_map<std::string, MONITORID> m_mMonitorIDMap;
void initServer(std::string socketName, int socketFd); void initServer(std::string socketName, int socketFd);
void startCompositor(); void startCompositor();
@ -91,10 +91,13 @@ class CCompositor {
bool m_bNextIsUnsafe = false; bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bIsShuttingDown = false; bool m_bIsShuttingDown = false;
bool m_bFinalRequests = false;
bool m_bDesktopEnvSet = false;
bool m_bEnableXwayland = true;
// ------------------------------------------------- // // ------------------------------------------------- //
CMonitor* getMonitorFromID(const int&); CMonitor* getMonitorFromID(const MONITORID&);
CMonitor* getMonitorFromName(const std::string&); CMonitor* getMonitorFromName(const std::string&);
CMonitor* getMonitorFromDesc(const std::string&); CMonitor* getMonitorFromDesc(const std::string&);
CMonitor* getMonitorFromCursor(); CMonitor* getMonitorFromCursor();
@ -114,38 +117,38 @@ class CCompositor {
PHLWINDOW getWindowFromHandle(uint32_t); PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE); bool isWorkspaceVisible(PHLWORKSPACE);
bool isWorkspaceVisibleNotCovered(PHLWORKSPACE); bool isWorkspaceVisibleNotCovered(PHLWORKSPACE);
PHLWORKSPACE getWorkspaceByID(const int&); PHLWORKSPACE getWorkspaceByID(const WORKSPACEID&);
PHLWORKSPACE getWorkspaceByName(const std::string&); PHLWORKSPACE getWorkspaceByName(const std::string&);
PHLWORKSPACE getWorkspaceByString(const std::string&); PHLWORKSPACE getWorkspaceByString(const std::string&);
void sanityCheckWorkspaces(); void sanityCheckWorkspaces();
void updateWorkspaceWindowDecos(const int&); void updateWorkspaceWindowDecos(const WORKSPACEID&);
void updateWorkspaceWindowData(const int&); void updateWorkspaceWindowData(const WORKSPACEID&);
int getWindowsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {}); int getWindowsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroupsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {}); int getGroupsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
PHLWINDOW getUrgentWindow(); PHLWINDOW getUrgentWindow();
bool hasUrgentWindowOnWorkspace(const int&); bool hasUrgentWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFirstWindowOnWorkspace(const int&); PHLWINDOW getFirstWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getTopLeftWindowOnWorkspace(const int&); PHLWINDOW getTopLeftWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFullscreenWindowOnWorkspace(const int&); PHLWINDOW getFullscreenWindowOnWorkspace(const WORKSPACEID&);
bool isWindowActive(PHLWINDOW); bool isWindowActive(PHLWINDOW);
void changeWindowZOrder(PHLWINDOW, bool); void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const int& monid); void cleanupFadingOut(const MONITORID& monid);
PHLWINDOW getWindowInDirection(PHLWINDOW, char); PHLWINDOW getWindowInDirection(PHLWINDOW, char);
PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}); PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {}); PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional<bool> floating = {});
int getNextAvailableNamedWorkspace(); WORKSPACEID getNextAvailableNamedWorkspace();
bool isPointOnAnyMonitor(const Vector2D&); bool isPointOnAnyMonitor(const Vector2D&);
bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr); bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CMonitor* getMonitorInDirection(const char&); CMonitor* getMonitorInDirection(const char&);
CMonitor* getMonitorInDirection(CMonitor*, const char&); CMonitor* getMonitorInDirection(CMonitor*, const char&);
void updateAllWindowsAnimatedDecorationValues(); void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const int64_t& id); void updateWorkspaceWindows(const WORKSPACEID& id);
void updateWindowAnimatedDecorationValues(PHLWINDOW); void updateWindowAnimatedDecorationValues(PHLWINDOW);
int getNextAvailableMonitorID(std::string const& name); MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, CMonitor*, bool noWarpCursor = false); void moveWorkspaceToMonitor(PHLWORKSPACE, 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 WORKSPACEID&);
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state); void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state);
@ -162,12 +165,13 @@ class CCompositor {
PHLLS getLayerSurfaceFromSurface(SP<CWLSurfaceResource>); PHLLS getLayerSurfaceFromSurface(SP<CWLSurfaceResource>);
void closeWindow(PHLWINDOW); void closeWindow(PHLWINDOW);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&); void forceReportSizesToWindowsOnWorkspace(const WORKSPACEID&);
PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused! PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "",
void renameWorkspace(const int&, const std::string& name = ""); bool isEmpty = true); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const WORKSPACEID&, const std::string& name = "");
void setActiveMonitor(CMonitor*); void setActiveMonitor(CMonitor*);
bool isWorkspaceSpecial(const int&); bool isWorkspaceSpecial(const WORKSPACEID&);
int getNewSpecialID(); WORKSPACEID getNewSpecialID();
void performUserChecks(); void performUserChecks();
void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace);
PHLWINDOW getForceFocus(); PHLWINDOW getForceFocus();

View file

@ -52,4 +52,8 @@ struct SHyprCtlCommand {
std::function<std::string(eHyprCtlOutputFormat, std::string)> fn; std::function<std::string(eHyprCtlOutputFormat, std::string)> fn;
}; };
typedef int64_t WINDOWID;
typedef int64_t MONITORID;
typedef int64_t WORKSPACEID;
typedef std::function<void(void*, SCallbackInfo&, std::any)> HOOK_CALLBACK_FN; typedef std::function<void(void*, SCallbackInfo&, std::any)> HOOK_CALLBACK_FN;

File diff suppressed because it is too large Load diff

View file

@ -3,8 +3,11 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp" #include "config/ConfigDataValues.hpp"
#include "config/ConfigValue.hpp"
#include "helpers/varlist/VarList.hpp" #include "helpers/varlist/VarList.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../xwayland/XWayland.hpp"
#include "../protocols/OutputManagement.hpp"
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -29,6 +32,8 @@ using namespace Hyprutils::String;
extern "C" char** environ; extern "C" char** environ;
#include "ConfigDescriptions.hpp"
static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) { static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) {
std::string V = VALUE; std::string V = VALUE;
@ -42,7 +47,7 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void**
std::string parseError = ""; std::string parseError = "";
for (auto& var : varlist) { for (auto const& var : varlist) {
if (var.find("deg") != std::string::npos) { if (var.find("deg") != std::string::npos) {
// last arg // last arg
try { try {
@ -137,6 +142,18 @@ static Hyprlang::CParseResult handleExecOnce(const char* c, const char* v) {
return result; return result;
} }
static Hyprlang::CParseResult handleExecShutdown(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleExecShutdown(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleMonitor(const char* c, const char* v) { static Hyprlang::CParseResult handleMonitor(const char* c, const char* v) {
const std::string VALUE = v; const std::string VALUE = v;
const std::string COMMAND = c; const std::string COMMAND = c;
@ -311,8 +328,6 @@ CConfigManager::CConfigManager() {
configPaths.emplace_back(getMainConfigPath()); configPaths.emplace_back(getMainConfigPath());
m_pConfig = std::make_unique<Hyprlang::CConfig>(configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); m_pConfig = std::make_unique<Hyprlang::CConfig>(configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
m_pConfig->addConfigValue("general:sensitivity", {1.0f});
m_pConfig->addConfigValue("general:apply_sens_to_raw", Hyprlang::INT{0});
m_pConfig->addConfigValue("general:border_size", Hyprlang::INT{1}); m_pConfig->addConfigValue("general:border_size", Hyprlang::INT{1});
m_pConfig->addConfigValue("general:no_border_on_floating", Hyprlang::INT{0}); m_pConfig->addConfigValue("general:no_border_on_floating", Hyprlang::INT{0});
m_pConfig->addConfigValue("general:border_part_of_window", Hyprlang::INT{1}); m_pConfig->addConfigValue("general:border_part_of_window", Hyprlang::INT{1});
@ -346,7 +361,6 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY}); m_pConfig->addConfigValue("misc:swallow_regex", {STRVAL_EMPTY});
m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY}); m_pConfig->addConfigValue("misc:swallow_exception_regex", {STRVAL_EMPTY});
m_pConfig->addConfigValue("misc:focus_on_activate", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:focus_on_activate", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:no_direct_scanout", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:mouse_move_focuses_monitor", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:render_ahead_of_time", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:render_ahead_of_time", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:render_ahead_safezone", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:render_ahead_safezone", Hyprlang::INT{1});
@ -357,9 +371,15 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("misc:exit_window_retains_fullscreen", Hyprlang::INT{0}); m_pConfig->addConfigValue("misc:exit_window_retains_fullscreen", Hyprlang::INT{0});
m_pConfig->addConfigValue("misc:initial_workspace_tracking", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:initial_workspace_tracking", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1}); m_pConfig->addConfigValue("misc:middle_click_paste", Hyprlang::INT{1});
m_pConfig->addConfigValue("misc:render_unfocused_fps", Hyprlang::INT{15});
m_pConfig->addConfigValue("misc:disable_xdg_env_checks", Hyprlang::INT{0});
m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:insert_after_current", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:focus_removed_window", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:merge_groups_on_drag", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:merge_floated_into_tiled_on_groupbar", Hyprlang::INT{0});
m_pConfig->addConfigValue("group:auto_group", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:drag_into_group", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:enabled", Hyprlang::INT{1}); m_pConfig->addConfigValue("group:groupbar:enabled", Hyprlang::INT{1});
m_pConfig->addConfigValue("group:groupbar:font_family", {STRVAL_EMPTY}); m_pConfig->addConfigValue("group:groupbar:font_family", {STRVAL_EMPTY});
m_pConfig->addConfigValue("group:groupbar:font_size", Hyprlang::INT{8}); m_pConfig->addConfigValue("group:groupbar:font_size", Hyprlang::INT{8});
@ -426,9 +446,9 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("dwindle:preserve_split", Hyprlang::INT{0}); m_pConfig->addConfigValue("dwindle:preserve_split", Hyprlang::INT{0});
m_pConfig->addConfigValue("dwindle:special_scale_factor", {1.f}); m_pConfig->addConfigValue("dwindle:special_scale_factor", {1.f});
m_pConfig->addConfigValue("dwindle:split_width_multiplier", {1.0f}); m_pConfig->addConfigValue("dwindle:split_width_multiplier", {1.0f});
m_pConfig->addConfigValue("dwindle:no_gaps_when_only", Hyprlang::INT{0});
m_pConfig->addConfigValue("dwindle:use_active_for_splits", Hyprlang::INT{1}); m_pConfig->addConfigValue("dwindle:use_active_for_splits", Hyprlang::INT{1});
m_pConfig->addConfigValue("dwindle:default_split_ratio", {1.f}); m_pConfig->addConfigValue("dwindle:default_split_ratio", {1.f});
m_pConfig->addConfigValue("dwindle:split_bias", Hyprlang::INT{0});
m_pConfig->addConfigValue("dwindle:smart_split", Hyprlang::INT{0}); m_pConfig->addConfigValue("dwindle:smart_split", Hyprlang::INT{0});
m_pConfig->addConfigValue("dwindle:smart_resizing", Hyprlang::INT{1}); m_pConfig->addConfigValue("dwindle:smart_resizing", Hyprlang::INT{1});
@ -438,7 +458,6 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("master:always_center_master", Hyprlang::INT{0}); m_pConfig->addConfigValue("master:always_center_master", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:new_on_active", {"none"}); m_pConfig->addConfigValue("master:new_on_active", {"none"});
m_pConfig->addConfigValue("master:new_on_top", Hyprlang::INT{0}); m_pConfig->addConfigValue("master:new_on_top", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:no_gaps_when_only", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:orientation", {"left"}); m_pConfig->addConfigValue("master:orientation", {"left"});
m_pConfig->addConfigValue("master:inherit_fullscreen", Hyprlang::INT{1}); m_pConfig->addConfigValue("master:inherit_fullscreen", Hyprlang::INT{1});
m_pConfig->addConfigValue("master:allow_small_split", Hyprlang::INT{0}); m_pConfig->addConfigValue("master:allow_small_split", Hyprlang::INT{0});
@ -449,6 +468,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("animations:first_launch_animation", Hyprlang::INT{1}); m_pConfig->addConfigValue("animations:first_launch_animation", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:follow_mouse", Hyprlang::INT{1}); m_pConfig->addConfigValue("input:follow_mouse", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:focus_on_close", Hyprlang::INT{0});
m_pConfig->addConfigValue("input:mouse_refocus", Hyprlang::INT{1}); m_pConfig->addConfigValue("input:mouse_refocus", Hyprlang::INT{1});
m_pConfig->addConfigValue("input:special_fallthrough", Hyprlang::INT{0}); m_pConfig->addConfigValue("input:special_fallthrough", Hyprlang::INT{0});
m_pConfig->addConfigValue("input:off_window_axis_events", Hyprlang::INT{1}); m_pConfig->addConfigValue("input:off_window_axis_events", Hyprlang::INT{1});
@ -521,6 +541,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("gestures:workspace_swipe_touch", Hyprlang::INT{0}); m_pConfig->addConfigValue("gestures:workspace_swipe_touch", Hyprlang::INT{0});
m_pConfig->addConfigValue("gestures:workspace_swipe_touch_invert", Hyprlang::INT{0}); m_pConfig->addConfigValue("gestures:workspace_swipe_touch_invert", Hyprlang::INT{0});
m_pConfig->addConfigValue("xwayland:enabled", Hyprlang::INT{1});
m_pConfig->addConfigValue("xwayland:use_nearest_neighbor", Hyprlang::INT{1}); m_pConfig->addConfigValue("xwayland:use_nearest_neighbor", Hyprlang::INT{1});
m_pConfig->addConfigValue("xwayland:force_zero_scaling", Hyprlang::INT{0}); m_pConfig->addConfigValue("xwayland:force_zero_scaling", Hyprlang::INT{0});
@ -531,7 +552,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:no_break_fs_vrr", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:min_refresh_rate", Hyprlang::INT{24}); m_pConfig->addConfigValue("cursor:min_refresh_rate", Hyprlang::INT{24});
m_pConfig->addConfigValue("cursor:hotspot_padding", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:hotspot_padding", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:inactive_timeout", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:inactive_timeout", {0.f});
m_pConfig->addConfigValue("cursor:no_warps", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:no_warps", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:persistent_warps", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:persistent_warps", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:warp_on_change_workspace", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:warp_on_change_workspace", Hyprlang::INT{0});
@ -539,6 +560,7 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("cursor:zoom_factor", {1.f}); m_pConfig->addConfigValue("cursor:zoom_factor", {1.f});
m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:enable_hyprcursor", Hyprlang::INT{1}); m_pConfig->addConfigValue("cursor:enable_hyprcursor", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:sync_gsettings_theme", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:hide_on_key_press", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:hide_on_key_press", Hyprlang::INT{0});
m_pConfig->addConfigValue("cursor:hide_on_touch", Hyprlang::INT{1}); m_pConfig->addConfigValue("cursor:hide_on_touch", Hyprlang::INT{1});
m_pConfig->addConfigValue("cursor:allow_dumb_copy", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:allow_dumb_copy", Hyprlang::INT{0});
@ -560,7 +582,9 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); m_pConfig->addConfigValue("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"});
m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); m_pConfig->addConfigValue("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"});
m_pConfig->addConfigValue("experimental:explicit_sync", Hyprlang::INT{0}); m_pConfig->addConfigValue("render:explicit_sync", Hyprlang::INT{2});
m_pConfig->addConfigValue("render:explicit_sync_kms", Hyprlang::INT{2});
m_pConfig->addConfigValue("render:direct_scanout", Hyprlang::INT{0});
// devices // devices
m_pConfig->addSpecialCategory("device", {"name"}); m_pConfig->addSpecialCategory("device", {"name"});
@ -601,6 +625,7 @@ CConfigManager::CConfigManager() {
// keywords // keywords
m_pConfig->registerHandler(&::handleRawExec, "exec", {false}); m_pConfig->registerHandler(&::handleRawExec, "exec", {false});
m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false}); m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false});
m_pConfig->registerHandler(&::handleExecShutdown, "exec-shutdown", {false});
m_pConfig->registerHandler(&::handleMonitor, "monitor", {false}); m_pConfig->registerHandler(&::handleMonitor, "monitor", {false});
m_pConfig->registerHandler(&::handleBind, "bind", {true}); m_pConfig->registerHandler(&::handleBind, "bind", {true});
m_pConfig->registerHandler(&::handleUnbind, "unbind", {false}); m_pConfig->registerHandler(&::handleUnbind, "unbind", {false});
@ -661,6 +686,10 @@ std::string CConfigManager::getMainConfigPath() {
if (!g_pCompositor->explicitConfigPath.empty()) if (!g_pCompositor->explicitConfigPath.empty())
return g_pCompositor->explicitConfigPath; return g_pCompositor->explicitConfigPath;
if (const auto CFG_ENV = getenv("HYPRLAND_CONFIG"); CFG_ENV)
return CFG_ENV;
Debug::log(TRACE, "Seems as if HYPRLAND_CONFIG isn't set, let's see what we can do with HOME.");
static const auto paths = Hyprutils::Path::findConfig(ISDEBUG ? "hyprlandd" : "hyprland"); static const auto paths = Hyprutils::Path::findConfig(ISDEBUG ? "hyprlandd" : "hyprland");
if (paths.first.has_value()) { if (paths.first.has_value()) {
return paths.first.value(); return paths.first.value();
@ -720,7 +749,6 @@ void CConfigManager::setDefaultAnimationVars() {
INITANIMCFG("fade"); INITANIMCFG("fade");
INITANIMCFG("border"); INITANIMCFG("border");
INITANIMCFG("borderangle"); INITANIMCFG("borderangle");
INITANIMCFG("workspaces");
// windows // windows
INITANIMCFG("windowsIn"); INITANIMCFG("windowsIn");
@ -741,7 +769,12 @@ void CConfigManager::setDefaultAnimationVars() {
// border // border
// workspaces // workspaces
INITANIMCFG("workspaces");
INITANIMCFG("workspacesIn");
INITANIMCFG("workspacesOut");
INITANIMCFG("specialWorkspace"); INITANIMCFG("specialWorkspace");
INITANIMCFG("specialWorkspaceIn");
INITANIMCFG("specialWorkspaceOut");
} }
// init the values // init the values
@ -770,7 +803,11 @@ void CConfigManager::setDefaultAnimationVars() {
CREATEANIMCFG("fadeLayersIn", "fadeLayers"); CREATEANIMCFG("fadeLayersIn", "fadeLayers");
CREATEANIMCFG("fadeLayersOut", "fadeLayers"); CREATEANIMCFG("fadeLayersOut", "fadeLayers");
CREATEANIMCFG("workspacesIn", "workspaces");
CREATEANIMCFG("workspacesOut", "workspaces");
CREATEANIMCFG("specialWorkspace", "workspaces"); CREATEANIMCFG("specialWorkspace", "workspaces");
CREATEANIMCFG("specialWorkspaceIn", "specialWorkspace");
CREATEANIMCFG("specialWorkspaceOut", "specialWorkspace");
} }
std::optional<std::string> CConfigManager::resetHLConfig() { std::optional<std::string> CConfigManager::resetHLConfig() {
@ -785,6 +822,7 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
m_vDeclaredPlugins.clear(); m_vDeclaredPlugins.clear();
m_dLayerRules.clear(); m_dLayerRules.clear();
m_vFailedPluginConfigValues.clear(); m_vFailedPluginConfigValues.clear();
finalExecRequests.clear();
// paths // paths
configPaths.clear(); configPaths.clear();
@ -798,11 +836,14 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
} }
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
for (auto& w : g_pCompositor->m_vWindows) { static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static int prevEnabledExplicit = *PENABLEEXPLICIT;
for (auto const& w : g_pCompositor->m_vWindows) {
w->uncacheWindowDecos(); w->uncacheWindowDecos();
} }
for (auto& m : g_pCompositor->m_vMonitors) for (auto const& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
// Update the keyboard layout to the cfg'd one if this is not the first launch // Update the keyboard layout to the cfg'd one if this is not the first launch
@ -826,8 +867,12 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
if (result.error && !std::any_cast<Hyprlang::INT>(m_pConfig->getConfigValue("debug:suppress_errors"))) if (result.error && !std::any_cast<Hyprlang::INT>(m_pConfig->getConfigValue("debug:suppress_errors")))
g_pHyprError->queueCreate(result.getError(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); g_pHyprError->queueCreate(result.getError(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0));
else if (std::any_cast<Hyprlang::INT>(m_pConfig->getConfigValue("autogenerated")) == 1) else if (std::any_cast<Hyprlang::INT>(m_pConfig->getConfigValue("autogenerated")) == 1)
g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + getMainConfigPath() + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland", g_pHyprError->queueCreate(
"Warning: You're using an autogenerated config! (config file: " + getMainConfigPath() +
" )\nSUPER+Q -> kitty (if it doesn't launch, make sure it's installed or choose a different terminal in the config)\nSUPER+M -> exit Hyprland",
CColor(1.0, 1.0, 70.0 / 255.0, 1.0)); CColor(1.0, 1.0, 70.0 / 255.0, 1.0));
else if (*PENABLEEXPLICIT != prevEnabledExplicit)
g_pHyprError->queueCreate("Warning: You changed the render:explicit_sync option, this requires you to restart Hyprland.", CColor(0.9, 0.76, 0.221, 1.0));
else else
g_pHyprError->destroy(); g_pHyprError->destroy();
@ -842,11 +887,34 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
ensureVRR(); ensureVRR();
} }
#ifndef NO_XWAYLAND
const auto PENABLEXWAYLAND = std::any_cast<Hyprlang::INT>(m_pConfig->getConfigValue("xwayland:enabled"));
// enable/disable xwayland usage
if (!isFirstLaunch) {
bool prevEnabledXwayland = g_pCompositor->m_bEnableXwayland;
if (PENABLEXWAYLAND != prevEnabledXwayland) {
g_pCompositor->m_bEnableXwayland = PENABLEXWAYLAND;
if (PENABLEXWAYLAND) {
Debug::log(LOG, "xwayland has been enabled");
} else {
Debug::log(LOG, "xwayland has been disabled, cleaning up...");
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_pXDGSurface || !w->m_bIsX11)
continue;
g_pCompositor->closeWindow(w);
}
}
g_pXWayland = std::make_unique<CXWayland>(g_pCompositor->m_bEnableXwayland);
}
} else
g_pCompositor->m_bEnableXwayland = PENABLEXWAYLAND;
#endif
if (!isFirstLaunch && !g_pCompositor->m_bUnsafeState) if (!isFirstLaunch && !g_pCompositor->m_bUnsafeState)
refreshGroupBarGradients(); refreshGroupBarGradients();
// Updates dynamic window and workspace rules // Updates dynamic window and workspace rules
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->inert()) if (w->inert())
continue; continue;
g_pCompositor->updateWorkspaceWindows(w->m_iID); g_pCompositor->updateWorkspaceWindows(w->m_iID);
@ -874,7 +942,7 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
Debug::coloredLogs = reinterpret_cast<int64_t* const*>(m_pConfig->getConfigValuePtr("debug:colored_stdout_logs")->getDataStaticPtr()); Debug::coloredLogs = reinterpret_cast<int64_t* const*>(m_pConfig->getConfigValuePtr("debug:colored_stdout_logs")->getDataStaticPtr());
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
// mark blur dirty // mark blur dirty
g_pHyprOpenGL->markBlurDirtyForMonitor(m.get()); g_pHyprOpenGL->markBlurDirtyForMonitor(m.get());
@ -884,7 +952,7 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
m->forceFullFrames = 2; m->forceFullFrames = 2;
// also force mirrors, as the aspect ratio could've changed // also force mirrors, as the aspect ratio could've changed
for (auto& mirror : m->mirrors) for (auto const& mirror : m->mirrors)
mirror->forceFullFrames = 3; mirror->forceFullFrames = 3;
} }
@ -916,14 +984,24 @@ void CConfigManager::init() {
} }
std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) { std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static int prevEnabledExplicit = *PENABLEEXPLICIT;
const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str()); const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str());
// invalidate layouts if they changed // invalidate layouts if they changed
if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) {
for (auto& m : g_pCompositor->m_vMonitors) for (auto const& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
} }
if (COMMAND.contains("explicit")) {
if (*PENABLEEXPLICIT != prevEnabledExplicit)
g_pHyprError->queueCreate("Warning: You changed the render:explicit_sync option, this requires you to restart Hyprland.", CColor(0.9, 0.76, 0.221, 1.0));
else
g_pHyprError->destroy();
}
// Update window border colors // Update window border colors
g_pCompositor->updateAllWindowsAnimatedDecorationValues(); g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@ -951,7 +1029,7 @@ void CConfigManager::tick() {
bool parse = false; bool parse = false;
for (auto& cf : configPaths) { for (auto const& cf : configPaths) {
struct stat fileStat; struct stat fileStat;
int err = stat(cf.c_str(), &fileStat); int err = stat(cf.c_str(), &fileStat);
if (err != 0) { if (err != 0) {
@ -1006,33 +1084,74 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s
return VAL; return VAL;
} }
SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { SMonitorRule CConfigManager::getMonitorRuleFor(const SP<CMonitor> PMONITOR) {
for (auto& r : m_dMonitorRules | std::views::reverse) { auto applyWlrOutputConfig = [PMONITOR](SMonitorRule rule) -> SMonitorRule {
if (PMONITOR.matchesStaticSelector(r.name)) { const auto CONFIG = PROTO::outputManagement->getOutputStateFor(PMONITOR);
return r;
if (!CONFIG)
return rule;
Debug::log(LOG, "CConfigManager::getMonitorRuleFor: found a wlr_output_manager override for {}", PMONITOR->szName);
Debug::log(LOG, " > overriding enabled: {} -> {}", !rule.disabled, !CONFIG->enabled);
rule.disabled = !CONFIG->enabled;
if ((CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_MODE) || (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE)) {
Debug::log(LOG, " > overriding mode: {:.0f}x{:.0f}@{:.2f}Hz -> {:.0f}x{:.0f}@{:.2f}Hz", rule.resolution.x, rule.resolution.y, rule.refreshRate, CONFIG->resolution.x,
CONFIG->resolution.y, CONFIG->refresh / 1000.F);
rule.resolution = CONFIG->resolution;
rule.refreshRate = CONFIG->refresh / 1000.F;
}
if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) {
Debug::log(LOG, " > overriding offset: {:.0f}, {:.0f} -> {:.0f}, {:.0f}", rule.offset.x, rule.offset.y, CONFIG->position.x, CONFIG->position.y);
rule.offset = CONFIG->position;
}
if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) {
Debug::log(LOG, " > overriding transform: {} -> {}", (uint8_t)rule.transform, (uint8_t)CONFIG->transform);
rule.transform = CONFIG->transform;
}
if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) {
Debug::log(LOG, " > overriding scale: {} -> {}", (uint8_t)rule.scale, (uint8_t)CONFIG->scale);
rule.scale = CONFIG->scale;
}
if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) {
Debug::log(LOG, " > overriding vrr: {} -> {}", rule.vrr.value_or(0), CONFIG->adaptiveSync);
rule.vrr = (int)CONFIG->adaptiveSync;
}
return rule;
};
for (auto const& r : m_dMonitorRules | std::views::reverse) {
if (PMONITOR->matchesStaticSelector(r.name)) {
return applyWlrOutputConfig(r);
} }
} }
Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR.szName); Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR->szName);
for (auto& r : m_dMonitorRules) { for (auto const& r : m_dMonitorRules) {
if (r.name.empty()) { if (r.name.empty()) {
return r; return applyWlrOutputConfig(r);
} }
} }
Debug::log(WARN, "No rules configured. Using the default hardcoded one."); Debug::log(WARN, "No rules configured. Using the default hardcoded one.");
return SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT, return applyWlrOutputConfig(SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT,
.name = "", .name = "",
.resolution = Vector2D(0, 0), .resolution = Vector2D(0, 0),
.offset = Vector2D(-INT32_MAX, -INT32_MAX), .offset = Vector2D(-INT32_MAX, -INT32_MAX),
.scale = -1}; // 0, 0 is preferred and -1, -1 is auto .scale = -1}); // 0, 0 is preferred and -1, -1 is auto
} }
SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) { SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) {
SWorkspaceRule mergedRule{}; SWorkspaceRule mergedRule{};
for (auto& rule : m_dWorkspaceRules) { for (auto const& rule : m_dWorkspaceRules) {
if (!pWorkspace->matchesStaticSelector(rule.workspaceString)) if (!pWorkspace->matchesStaticSelector(rule.workspaceString))
continue; continue;
@ -1105,7 +1224,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
// local tags for dynamic tag rule match // local tags for dynamic tag rule match
auto tags = pWindow->m_tags; auto tags = pWindow->m_tags;
for (auto& rule : m_dWindowRules) { for (auto const& rule : m_dWindowRules) {
// check if we have a matching rule // check if we have a matching rule
if (!rule.v2) { if (!rule.v2) {
try { try {
@ -1185,6 +1304,32 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
continue; continue;
} }
if (!rule.szFullscreenState.empty()) {
const auto ARGS = CVarList(rule.szFullscreenState, 2, ' ');
//
std::optional<eFullscreenMode> internalMode, clientMode;
if (ARGS[0] == "*")
internalMode = std::nullopt;
else if (isNumber(ARGS[0]))
internalMode = (eFullscreenMode)std::stoi(ARGS[0]);
else
throw std::runtime_error("szFullscreenState internal mode not valid");
if (ARGS[1] == "*")
clientMode = std::nullopt;
else if (isNumber(ARGS[1]))
clientMode = (eFullscreenMode)std::stoi(ARGS[1]);
else
throw std::runtime_error("szFullscreenState client mode not valid");
if (internalMode.has_value() && pWindow->m_sFullscreenState.internal != internalMode)
continue;
if (clientMode.has_value() && pWindow->m_sFullscreenState.client != clientMode)
continue;
}
if (!rule.szOnWorkspace.empty()) { if (!rule.szOnWorkspace.empty()) {
const auto PWORKSPACE = pWindow->m_pWorkspace; const auto PWORKSPACE = pWindow->m_pWorkspace;
if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule.szOnWorkspace)) if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule.szOnWorkspace))
@ -1244,7 +1389,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
bool anyExecFound = false; bool anyExecFound = false;
for (auto& er : execRequestedRules) { for (auto const& er : execRequestedRules) {
if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) { if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) {
returns.push_back({er.szRule, "execRule"}); returns.push_back({er.szRule, "execRule"});
anyExecFound = true; anyExecFound = true;
@ -1264,7 +1409,7 @@ std::vector<SLayerRule> CConfigManager::getMatchingRules(PHLLS pLS) {
if (!pLS->layerSurface || pLS->fadingOut) if (!pLS->layerSurface || pLS->fadingOut)
return returns; return returns;
for (auto& lr : m_dLayerRules) { for (auto const& lr : m_dLayerRules) {
if (lr.targetNamespace.starts_with("address:0x")) { if (lr.targetNamespace.starts_with("address:0x")) {
if (std::format("address:0x{:x}", (uintptr_t)pLS.get()) != lr.targetNamespace) if (std::format("address:0x{:x}", (uintptr_t)pLS.get()) != lr.targetNamespace)
continue; continue;
@ -1301,7 +1446,7 @@ void CConfigManager::dispatchExecOnce() {
firstExecDispatched = true; firstExecDispatched = true;
isLaunchingExecOnce = true; isLaunchingExecOnce = true;
for (auto& c : firstExecRequests) { for (auto const& c : firstExecRequests) {
handleRawExec("", c); handleRawExec("", c);
} }
@ -1318,6 +1463,24 @@ void CConfigManager::dispatchExecOnce() {
g_pCompositor->performUserChecks(); g_pCompositor->performUserChecks();
} }
void CConfigManager::dispatchExecShutdown() {
if (finalExecRequests.empty()) {
g_pCompositor->m_bFinalRequests = false;
return;
}
g_pCompositor->m_bFinalRequests = true;
for (auto const& c : finalExecRequests) {
handleExecShutdown("", c);
}
finalExecRequests.clear();
// Actually exit now
handleExecShutdown("", "hyprctl dispatch exit");
}
void CConfigManager::appendMonitorRule(const SMonitorRule& r) { void CConfigManager::appendMonitorRule(const SMonitorRule& r) {
m_dMonitorRules.emplace_back(r); m_dMonitorRules.emplace_back(r);
} }
@ -1338,11 +1501,11 @@ void CConfigManager::performMonitorReload() {
bool overAgain = false; bool overAgain = false;
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto const& m : g_pCompositor->m_vRealMonitors) {
if (!m->output || m->isUnsafeFallback) if (!m->output || m->isUnsafeFallback)
continue; continue;
auto rule = getMonitorRuleFor(*m); auto rule = getMonitorRuleFor(m);
if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) { if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) {
overAgain = true; overAgain = true;
@ -1385,7 +1548,7 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) {
} }
bool CConfigManager::shouldBlurLS(const std::string& ns) { bool CConfigManager::shouldBlurLS(const std::string& ns) {
for (auto& bls : m_dBlurLSNamespaces) { for (auto const& bls : m_dBlurLSNamespaces) {
if (bls == ns) { if (bls == ns) {
return true; return true;
} }
@ -1395,11 +1558,11 @@ bool CConfigManager::shouldBlurLS(const std::string& ns) {
} }
void CConfigManager::ensureMonitorStatus() { void CConfigManager::ensureMonitorStatus() {
for (auto& rm : g_pCompositor->m_vRealMonitors) { for (auto const& rm : g_pCompositor->m_vRealMonitors) {
if (!rm->output || rm->isUnsafeFallback) if (!rm->output || rm->isUnsafeFallback)
continue; continue;
auto rule = getMonitorRuleFor(*rm); auto rule = getMonitorRuleFor(rm);
if (rule.disabled == rm->m_bEnabled) if (rule.disabled == rm->m_bEnabled)
g_pHyprRenderer->applyMonitorRule(rm.get(), &rule); g_pHyprRenderer->applyMonitorRule(rm.get(), &rule);
@ -1417,7 +1580,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
if (USEVRR == 0) { if (USEVRR == 0) {
if (m->vrrActive) { if (m->vrrActive) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false); m->output->state->setAdaptiveSync(false);
if (!m->state.commit()) if (!m->state.commit())
@ -1427,6 +1590,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
return; return;
} else if (USEVRR == 1) { } else if (USEVRR == 1) {
if (!m->vrrActive) { if (!m->vrrActive) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(true); m->output->state->setAdaptiveSync(true);
if (!m->state.test()) { if (!m->state.test()) {
@ -1451,6 +1615,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN); const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
if (WORKSPACEFULL) { if (WORKSPACEFULL) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(true); m->output->state->setAdaptiveSync(true);
if (!m->state.test()) { if (!m->state.test()) {
@ -1462,6 +1627,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name);
} else if (!WORKSPACEFULL) { } else if (!WORKSPACEFULL) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false); m->output->state->setAdaptiveSync(false);
if (!m->state.commit()) if (!m->state.commit())
@ -1475,7 +1641,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
return; return;
} }
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
ensureVRRForDisplay(m.get()); ensureVRRForDisplay(m.get());
} }
} }
@ -1497,7 +1663,7 @@ CMonitor* CConfigManager::getBoundMonitorForWS(const std::string& wsname) {
} }
std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname) { std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname) {
for (auto& wr : m_dWorkspaceRules) { for (auto const& wr : m_dWorkspaceRules) {
const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName; const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName;
if (WSNAME == wsname) if (WSNAME == wsname)
@ -1568,7 +1734,7 @@ void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, Hy
} }
void CConfigManager::removePluginConfig(HANDLE handle) { void CConfigManager::removePluginConfig(HANDLE handle) {
for (auto& k : pluginKeywords) { for (auto const& k : pluginKeywords) {
if (k.handle != handle) if (k.handle != handle)
continue; continue;
@ -1576,7 +1742,7 @@ void CConfigManager::removePluginConfig(HANDLE handle) {
} }
std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; }); std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; });
for (auto& [h, n] : pluginVariables) { for (auto const& [h, n] : pluginVariables) {
if (h != handle) if (h != handle)
continue; continue;
@ -1591,7 +1757,7 @@ std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) {
if (other->monitor == name) if (other->monitor == name)
return other->workspaceString; return other->workspaceString;
if (other->monitor.substr(0, 5) == "desc:") { if (other->monitor.substr(0, 5) == "desc:") {
auto monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5)); auto const monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5));
if (monitor && monitor->szName == name) if (monitor && monitor->szName == name)
return other->workspaceString; return other->workspaceString;
} }
@ -1617,6 +1783,16 @@ std::optional<std::string> CConfigManager::handleExecOnce(const std::string& com
return {}; return {};
} }
std::optional<std::string> CConfigManager::handleExecShutdown(const std::string& command, const std::string& args) {
if (g_pCompositor->m_bFinalRequests) {
g_pKeybindManager->spawn(args);
return {};
}
finalExecRequests.push_back(args);
return {};
}
static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) {
auto args = CVarList(modeline, 0, 's'); auto args = CVarList(modeline, 0, 's');
@ -1664,7 +1840,7 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) {
if (it != flagsmap.end()) if (it != flagsmap.end())
mode.flags |= it->second; mode.flags |= it->second;
else else
Debug::log(ERR, "invalid flag {} in modeline", it->first); Debug::log(ERR, "invalid flag {} in modeline", key);
} }
snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000);
@ -1988,7 +2164,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool dontInhibit = false; bool dontInhibit = false;
const auto BINDARGS = command.substr(4); const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) { for (auto const& arg : BINDARGS) {
if (arg == 'l') { if (arg == 'l') {
locked = true; locked = true;
} else if (arg == 'r') { } else if (arg == 'r') {
@ -2099,7 +2275,7 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
bool windowRuleValid(const std::string& RULE) { bool windowRuleValid(const std::string& RULE) {
static const auto rules = std::unordered_set<std::string>{ static const auto rules = std::unordered_set<std::string>{
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused",
}; };
static const auto rulesPrefix = std::vector<std::string>{ static const auto rulesPrefix = std::vector<std::string>{
"animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor", "animation", "bordercolor", "bordersize", "center", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
@ -2114,7 +2290,7 @@ bool windowRuleValid(const std::string& RULE) {
bool layerRuleValid(const std::string& RULE) { bool layerRuleValid(const std::string& RULE) {
static const auto rules = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"}; static const auto rules = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
static const auto rulesPrefix = std::vector<std::string>{"ignorealpha", "ignorezero", "xray", "animation"}; static const auto rulesPrefix = std::vector<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order"};
return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); }); return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); });
} }
@ -2166,9 +2342,9 @@ std::optional<std::string> CConfigManager::handleLayerRule(const std::string& co
m_dLayerRules.push_back({VALUE, RULE}); m_dLayerRules.push_back({VALUE, RULE});
for (auto& m : g_pCompositor->m_vMonitors) for (auto const& m : g_pCompositor->m_vMonitors)
for (auto& lsl : m->m_aLayerSurfaceLayers) for (auto const& lsl : m->m_aLayerSurfaceLayers)
for (auto& ls : lsl) for (auto const& ls : lsl)
ls->applyRules(); ls->applyRules();
return {}; return {};
@ -2199,6 +2375,7 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
const auto FULLSCREENPOS = VALUE.find("fullscreen:"); const auto FULLSCREENPOS = VALUE.find("fullscreen:");
const auto PINNEDPOS = VALUE.find("pinned:"); const auto PINNEDPOS = VALUE.find("pinned:");
const auto FOCUSPOS = VALUE.find("focus:"); const auto FOCUSPOS = VALUE.find("focus:");
const auto FULLSCREENSTATEPOS = VALUE.find("fullscreenstate:");
const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); const auto ONWORKSPACEPOS = VALUE.find("onworkspace:");
// find workspacepos that isn't onworkspacepos // find workspacepos that isn't onworkspacepos
@ -2212,9 +2389,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
currentPos = VALUE.find("workspace:", currentPos + 1); currentPos = VALUE.find("workspace:", currentPos + 1);
} }
const auto checkPos = std::unordered_set{ const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS,
TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, FULLSCREENPOS, PINNEDPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS};
};
if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) { if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) {
Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE);
return "Invalid rulev2 syntax: " + VALUE; return "Invalid rulev2 syntax: " + VALUE;
@ -2243,6 +2419,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
min = FULLSCREENPOS; min = FULLSCREENPOS;
if (PINNEDPOS > pos && PINNEDPOS < min) if (PINNEDPOS > pos && PINNEDPOS < min)
min = PINNEDPOS; min = PINNEDPOS;
if (FULLSCREENSTATEPOS > pos && FULLSCREENSTATEPOS < min)
min = FULLSCREENSTATEPOS;
if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min)
min = ONWORKSPACEPOS; min = ONWORKSPACEPOS;
if (WORKSPACEPOS > pos && WORKSPACEPOS < min) if (WORKSPACEPOS > pos && WORKSPACEPOS < min)
@ -2287,6 +2465,9 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
if (PINNEDPOS != std::string::npos) if (PINNEDPOS != std::string::npos)
rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0;
if (FULLSCREENSTATEPOS != std::string::npos)
rule.szFullscreenState = extract(FULLSCREENSTATEPOS + 16);
if (WORKSPACEPOS != std::string::npos) if (WORKSPACEPOS != std::string::npos)
rule.szWorkspace = extract(WORKSPACEPOS + 10); rule.szWorkspace = extract(WORKSPACEPOS + 10);
@ -2328,6 +2509,9 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
if (rule.bPinned != -1 && rule.bPinned != other.bPinned) if (rule.bPinned != -1 && rule.bPinned != other.bPinned)
return false; return false;
if (!rule.szFullscreenState.empty() && rule.szFullscreenState != other.szFullscreenState)
return false;
if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace) if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace)
return false; return false;
@ -2359,9 +2543,9 @@ void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBl
matchName = matchName.substr(8); matchName = matchName.substr(8);
} }
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) { for (auto const& lsl : m->m_aLayerSurfaceLayers) {
for (auto& ls : lsl) { for (auto const& ls : lsl) {
if (BYADDRESS) { if (BYADDRESS) {
if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName)
ls->forceBlur = forceBlur; ls->forceBlur = forceBlur;
@ -2414,7 +2598,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
// } // }
const static std::string ruleOnCreatedEmpty = "on-created-empty:"; const static std::string ruleOnCreatedEmpty = "on-created-empty:";
const static int ruleOnCreatedEmptyLen = ruleOnCreatedEmpty.length(); const static auto ruleOnCreatedEmptyLen = ruleOnCreatedEmpty.length();
auto assignRule = [&](std::string rule) -> std::optional<std::string> { auto assignRule = [&](std::string rule) -> std::optional<std::string> {
size_t delim = std::string::npos; size_t delim = std::string::npos;
@ -2470,7 +2654,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
}; };
CVarList rulesList{rules, 0, ',', true}; CVarList rulesList{rules, 0, ',', true};
for (auto& r : rulesList) { for (auto const& r : rulesList) {
const auto R = assignRule(r); const auto R = assignRule(r);
if (R.has_value()) if (R.has_value())
return R; return R;
@ -2586,3 +2770,60 @@ std::optional<std::string> CConfigManager::handlePlugin(const std::string& comma
return {}; return {};
} }
const std::vector<SConfigOptionDescription>& CConfigManager::getAllDescriptions() {
return CONFIG_OPTIONS;
}
std::string SConfigOptionDescription::jsonify() const {
auto parseData = [this]() -> std::string {
return std::visit(
[](auto&& val) {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, SStringData>) {
return std::format(R"#( "value": "{}")#", val.value);
} else if constexpr (std::is_same_v<T, SRangeData>) {
return std::format(R"#( "value": {},
"min": {},
"max": {})#",
val.value, val.min, val.max);
} else if constexpr (std::is_same_v<T, SFloatData>) {
return std::format(R"#( "value": {},
"min": {},
"max": {})#",
val.value, val.min, val.max);
} else if constexpr (std::is_same_v<T, SColorData>) {
return std::format(R"#( "value": {})#", val.color.getAsHex());
} else if constexpr (std::is_same_v<T, SBoolData>) {
return std::format(R"#( "value": {})#", val.value);
} else if constexpr (std::is_same_v<T, SChoiceData>) {
return std::format(R"#( "value": {})#", val.choices);
} else if constexpr (std::is_same_v<T, SVectorData>) {
return std::format(R"#( "x": {},
"y": {},
"min_x": {},
"min_y": {},
"max_x": {},
"max_y": {})#",
val.vec.x, val.vec.y, val.min.x, val.min.y, val.max.x, val.max.y);
} else if constexpr (std::is_same_v<T, SGradientData>) {
return std::format(R"#( "value": "{}")#", val.gradient);
}
return std::string{""};
},
data);
};
std::string json = std::format(R"#({{
"value": "{}",
"description": "{}",
"type": {},
"flags": {},
"data": {{
{}
}}
}})#",
value, description, (uint16_t)type, (uint32_t)flags, parseData());
return json;
}

View file

@ -6,6 +6,7 @@
#include "../debug/Log.hpp" #include "../debug/Log.hpp"
#include <unordered_map> #include <unordered_map>
#include "../defines.hpp" #include "../defines.hpp"
#include <variant>
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <algorithm> #include <algorithm>
@ -33,7 +34,7 @@ struct SWorkspaceRule {
std::string monitor = ""; std::string monitor = "";
std::string workspaceString = ""; std::string workspaceString = "";
std::string workspaceName = ""; std::string workspaceName = "";
int workspaceId = -1; WORKSPACEID workspaceId = -1;
bool isDefault = false; bool isDefault = false;
bool isPersistent = false; bool isPersistent = false;
std::optional<CCssGapData> gapsIn; std::optional<CCssGapData> gapsIn;
@ -83,6 +84,70 @@ struct SExecRequestedRule {
uint64_t iPid = 0; uint64_t iPid = 0;
}; };
enum eConfigOptionType : uint16_t {
CONFIG_OPTION_BOOL = 0,
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
CONFIG_OPTION_FLOAT = 2,
CONFIG_OPTION_STRING_SHORT = 3, /* e.g. "auto" */
CONFIG_OPTION_STRING_LONG = 4, /* e.g. a command */
CONFIG_OPTION_COLOR = 5,
CONFIG_OPTION_CHOICE = 6, /* e.g. "one", "two", "three" */
CONFIG_OPTION_GRADIENT = 7,
CONFIG_OPTION_VECTOR = 8,
};
enum eConfigOptionFlags : uint32_t {
CONFIG_OPTION_FLAG_PERCENTAGE = (1 << 0),
};
struct SConfigOptionDescription {
struct SBoolData {
bool value = false;
};
struct SRangeData {
int value = 0, min = 0, max = 2;
};
struct SFloatData {
float value = 0, min = 0, max = 100;
};
struct SStringData {
std::string value;
};
struct SColorData {
CColor color;
};
struct SChoiceData {
int firstIndex = 0;
std::string choices; // comma-separated
};
struct SGradientData {
std::string gradient;
};
struct SVectorData {
Vector2D vec, min, max;
};
std::string value; // e.g. general:gaps_in
std::string description;
std::string specialCategory; // if value is special (e.g. device:abc) value will be abc and special device
bool specialKey = false;
eConfigOptionType type = CONFIG_OPTION_BOOL;
uint32_t flags = 0; // eConfigOptionFlags
std::string jsonify() const;
//
std::variant<SBoolData, SRangeData, SFloatData, SStringData, SColorData, SChoiceData, SGradientData, SVectorData> data;
};
class CConfigManager { class CConfigManager {
public: public:
CConfigManager(); CConfigManager();
@ -104,7 +169,7 @@ class CConfigManager {
static std::string getMainConfigPath(); static std::string getMainConfigPath();
const std::string getConfigString(); const std::string getConfigString();
SMonitorRule getMonitorRuleFor(const CMonitor&); SMonitorRule getMonitorRuleFor(const SP<CMonitor>);
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
std::string getDefaultWorkspaceFor(const std::string&); std::string getDefaultWorkspaceFor(const std::string&);
@ -115,6 +180,8 @@ class CConfigManager {
std::vector<SWindowRule> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false); std::vector<SWindowRule> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
std::vector<SLayerRule> getMatchingRules(PHLLS); std::vector<SLayerRule> getMatchingRules(PHLLS);
const std::vector<SConfigOptionDescription>& getAllDescriptions();
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas; std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig(); std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
@ -125,6 +192,7 @@ class CConfigManager {
// no-op when done. // no-op when done.
void dispatchExecOnce(); void dispatchExecOnce();
void dispatchExecShutdown();
void performMonitorReload(); void performMonitorReload();
void appendMonitorRule(const SMonitorRule&); void appendMonitorRule(const SMonitorRule&);
@ -146,6 +214,7 @@ class CConfigManager {
// keywords // keywords
std::optional<std::string> handleRawExec(const std::string&, const std::string&); std::optional<std::string> handleRawExec(const std::string&, const std::string&);
std::optional<std::string> handleExecOnce(const std::string&, const std::string&); std::optional<std::string> handleExecOnce(const std::string&, const std::string&);
std::optional<std::string> handleExecShutdown(const std::string&, const std::string&);
std::optional<std::string> handleMonitor(const std::string&, const std::string&); std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&); std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&); std::optional<std::string> handleUnbind(const std::string&, const std::string&);
@ -222,6 +291,7 @@ class CConfigManager {
bool firstExecDispatched = false; bool firstExecDispatched = false;
bool m_bManualCrashInitiated = false; bool m_bManualCrashInitiated = false;
std::deque<std::string> firstExecRequests; std::deque<std::string> firstExecRequests;
std::deque<std::string> finalExecRequests;
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
std::string m_szConfigErrors = ""; std::string m_szConfigErrors = "";

View file

@ -130,6 +130,13 @@ animations {
animation = workspaces, 1, 6, default animation = workspaces, 1, 6, default
} }
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all three if you wish to use that.
# workspace = w[t1], gapsout:0, gapsin:0, border: 0, rounding:0
# workspace = w[tg1], gapsout:0, gapsin:0, border: 0, rounding:0
# workspace = f[1], gapsout:0, gapsin:0, border: 0, rounding:0
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle { dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@ -182,9 +189,9 @@ device {
} }
#################### ###################
### KEYBINDINGSS ### ### KEYBINDINGS ###
#################### ###################
# See https://wiki.hyprland.org/Configuring/Keywords/ # See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier $mainMod = SUPER # Sets "Windows" key as main modifier
@ -241,6 +248,19 @@ bind = $mainMod, mouse_up, workspace, e-1
bindm = $mainMod, mouse:272, movewindow bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPause, exec, playerctl play-pause
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioPrev, exec, playerctl previous
############################## ##############################
### WINDOWS AND WORKSPACES ### ### WINDOWS AND WORKSPACES ###
@ -255,5 +275,9 @@ bindm = $mainMod, mouse:273, resizewindow
# Example windowrule v2 # Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
windowrulev2 = suppressevent maximize, class:.* # You'll probably like this. # Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.*
# Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
)#"; )#";

View file

@ -86,7 +86,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
stderr.flush(); stderr.flush();
} }
reportFd = open(reportPath.get_str(), O_WRONLY | O_CREAT, S_IRWXU); reportFd = open(reportPath.get_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (reportFd < 0) { if (reportFd < 0) {
exit_with_error("Failed to open crash report path for writing"); exit_with_error("Failed to open crash report path for writing");
} }

View file

@ -57,7 +57,7 @@ static std::string formatToString(uint32_t drmFormat) {
static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) { static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) {
std::string result; std::string result;
for (auto& m : pMonitor->output->modes) { for (auto const& m : pMonitor->output->modes) {
if (format == FORMAT_NORMAL) if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0); result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
else else
@ -71,7 +71,7 @@ static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFor
std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) { std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
std::string result; std::string result;
if (!m->output || m->ID == -1ull) if (!m->output || m->ID == -1)
return ""; return "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@ -103,6 +103,7 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"focused": {}, "focused": {},
"dpmsStatus": {}, "dpmsStatus": {},
"vrr": {}, "vrr": {},
"solitary": "{:x}",
"activelyTearing": {}, "activelyTearing": {},
"disabled": {}, "disabled": {},
"currentFormat": "{}", "currentFormat": "{}",
@ -114,19 +115,20 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), m->activeSpecialWorkspaceID(), m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), m->activeSpecialWorkspaceID(),
escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y,
(int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m == g_pCompositor->m_pLastMonitor ? "true" : "false"), (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m == g_pCompositor->m_pLastMonitor ? "true" : "false"),
(m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (m->tearingState.activelyTearing ? "true" : "false"), (m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (uint64_t)m->solitaryClient.get(),
(m->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format)); (m->tearingState.activelyTearing ? "true" : "false"), (m->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat),
availableModesForOutput(m.get(), format));
} else { } else {
result += std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" result += std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t" "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", "dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\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->szShortDescription, m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription,
m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, m->tearingState.activelyTearing, (m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, (uint64_t)m->solitaryClient.get(),
!m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format)); m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
} }
return result; return result;
@ -146,7 +148,7 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { for (auto const& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
result += CHyprCtl::getMonitorData(m, format); result += CHyprCtl::getMonitorData(m, format);
} }
@ -154,20 +156,11 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "]"; result += "]";
} else { } else {
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { for (auto const& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output || m->ID == -1ull) if (!m->output || m->ID == -1)
continue; continue;
result += result += CHyprCtl::getMonitorData(m, format);
std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t"
"special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t"
"dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\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->szShortDescription,
m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform,
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, (int)(m->output->state ? m->output->state->state().adaptiveSync : false),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
} }
} }
@ -272,7 +265,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all) if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all)
continue; continue;
@ -283,7 +276,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "]"; result += "]";
} else { } else {
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all) if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all)
continue; continue;
@ -334,11 +327,13 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF
const std::string rounding = (bool)(r.noRounding) ? std::format(",\n \"rounding\": {}", boolToString(!r.noRounding.value())) : ""; const std::string rounding = (bool)(r.noRounding) ? std::format(",\n \"rounding\": {}", boolToString(!r.noRounding.value())) : "";
const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : ""; const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : "";
const std::string shadow = (bool)(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : ""; const std::string shadow = (bool)(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : "";
const std::string defaultName = r.defaultName.has_value() ? std::format(",\n \"defaultName\": \"{}\"", escapeJSONStrings(r.defaultName.value())) : "";
std::string result = std::format(R"#({{ std::string result =
"workspaceString": "{}"{}{}{}{}{}{}{}{} std::format(R"#({{
"workspaceString": "{}"{}{}{}{}{}{}{}{}{}{}{}
}})#", }})#",
escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate, shadow); escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, borderSize, border, rounding, decorate, shadow, defaultName);
return result; return result;
} else { } else {
@ -356,9 +351,10 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF
const std::string rounding = std::format("\trounding: {}\n", (bool)(r.noRounding) ? boolToString(!r.noRounding.value()) : "<unset>"); const std::string rounding = std::format("\trounding: {}\n", (bool)(r.noRounding) ? boolToString(!r.noRounding.value()) : "<unset>");
const std::string decorate = std::format("\tdecorate: {}\n", (bool)(r.decorate) ? boolToString(r.decorate.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.noShadow) ? boolToString(!r.noShadow.value()) : "<unset>"); const std::string shadow = std::format("\tshadow: {}\n", (bool)(r.noShadow) ? boolToString(!r.noShadow.value()) : "<unset>");
const std::string defaultName = std::format("\tdefaultName: {}\n", r.defaultName.value_or("<unset>"));
std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut,
borderSize, border, rounding, decorate, shadow); borderSize, border, rounding, decorate, shadow, defaultName);
return result; return result;
} }
@ -381,7 +377,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto const& w : g_pCompositor->m_vWorkspaces) {
result += CHyprCtl::getWorkspaceData(w, format); result += CHyprCtl::getWorkspaceData(w, format);
result += ","; result += ",";
} }
@ -389,7 +385,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
trimTrailingComma(result); trimTrailingComma(result);
result += "]"; result += "]";
} else { } else {
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto const& w : g_pCompositor->m_vWorkspaces) {
result += CHyprCtl::getWorkspaceData(w, format); result += CHyprCtl::getWorkspaceData(w, format);
} }
} }
@ -401,7 +397,7 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
std::string result = ""; std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) { for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format); result += getWorkspaceRuleData(r, format);
result += ","; result += ",";
} }
@ -409,7 +405,7 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
trimTrailingComma(result); trimTrailingComma(result);
result += "]"; result += "]";
} else { } else {
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) { for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format); result += getWorkspaceRuleData(r, format);
} }
} }
@ -437,7 +433,7 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n"; result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) { for (auto const& mon : g_pCompositor->m_vMonitors) {
result += std::format( result += std::format(
R"#("{}": {{ R"#("{}": {{
"levels": {{ "levels": {{
@ -445,13 +441,13 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
escapeJSONStrings(mon->szName)); escapeJSONStrings(mon->szName));
int layerLevel = 0; int layerLevel = 0;
for (auto& level : mon->m_aLayerSurfaceLayers) { for (auto const& level : mon->m_aLayerSurfaceLayers) {
result += std::format( result += std::format(
R"#( R"#(
"{}": [ "{}": [
)#", )#",
layerLevel); layerLevel);
for (auto& layer : level) { for (auto const& layer : level) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -484,14 +480,14 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n}\n"; result += "\n}\n";
} else { } else {
for (auto& mon : g_pCompositor->m_vMonitors) { for (auto const& mon : g_pCompositor->m_vMonitors) {
result += std::format("Monitor {}:\n", mon->szName); result += std::format("Monitor {}:\n", mon->szName);
int layerLevel = 0; int layerLevel = 0;
static const std::array<std::string, 4> levelNames = {"background", "bottom", "top", "overlay"}; static const std::array<std::string, 4> levelNames = {"background", "bottom", "top", "overlay"};
for (auto& level : mon->m_aLayerSurfaceLayers) { for (auto const& level : mon->m_aLayerSurfaceLayers) {
result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]); result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]);
for (auto& layer : level) { for (auto const& layer : level) {
result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}\n", (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}\n", (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width,
layer->geometry.height, layer->szNamespace); layer->geometry.height, layer->szNamespace);
} }
@ -510,7 +506,7 @@ std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) { for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format( result += std::format(
R"#( R"#(
"{}",)#", "{}",)#",
@ -520,7 +516,7 @@ std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n]\n"; result += "\n]\n";
} else { } else {
for (auto& m : g_pLayoutManager->getAllLayoutNames()) { for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format("{}\n", m); result += std::format("{}\n", m);
} }
} }
@ -557,7 +553,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "{\n"; result += "{\n";
result += "\"mice\": [\n"; result += "\"mice\": [\n";
for (auto& m : g_pInputManager->m_vPointers) { for (auto const& m : g_pInputManager->m_vPointers) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -572,7 +568,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n],\n"; result += "\n],\n";
result += "\"keyboards\": [\n"; result += "\"keyboards\": [\n";
for (auto& k : g_pInputManager->m_vKeyboards) { for (auto const& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout(); const auto KM = k->getActiveLayout();
result += std::format( result += std::format(
R"#( {{ R"#( {{
@ -596,7 +592,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"tablets\": [\n"; result += "\"tablets\": [\n";
for (auto& d : g_pInputManager->m_vTabletPads) { for (auto const& d : g_pInputManager->m_vTabletPads) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -609,7 +605,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
(uintptr_t)d.get(), (uintptr_t)d->parent.get(), escapeJSONStrings(d->parent ? d->parent->hlName : "")); (uintptr_t)d.get(), (uintptr_t)d->parent.get(), escapeJSONStrings(d->parent ? d->parent->hlName : ""));
} }
for (auto& d : g_pInputManager->m_vTablets) { for (auto const& d : g_pInputManager->m_vTablets) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -618,7 +614,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
(uintptr_t)d.get(), escapeJSONStrings(d->hlName)); (uintptr_t)d.get(), escapeJSONStrings(d->hlName));
} }
for (auto& d : g_pInputManager->m_vTabletTools) { for (auto const& d : g_pInputManager->m_vTabletTools) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -632,7 +628,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"touch\": [\n"; result += "\"touch\": [\n";
for (auto& d : g_pInputManager->m_vTouches) { for (auto const& d : g_pInputManager->m_vTouches) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -646,7 +642,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"switches\": [\n"; result += "\"switches\": [\n";
for (auto& d : g_pInputManager->m_lSwitches) { for (auto const& d : g_pInputManager->m_lSwitches) {
result += std::format( result += std::format(
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
@ -663,14 +659,14 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
} else { } else {
result += "mice:\n"; result += "mice:\n";
for (auto& m : g_pInputManager->m_vPointers) { for (auto const& m : g_pInputManager->m_vPointers) {
result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName, result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName,
(m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f)); (m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f));
} }
result += "\n\nKeyboards:\n"; result += "\n\nKeyboards:\n";
for (auto& k : g_pInputManager->m_vKeyboards) { for (auto const& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout(); const auto KM = k->getActiveLayout();
result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tmain: {}\n", result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tmain: {}\n",
(uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant, (uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant,
@ -679,27 +675,27 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nTablets:\n"; result += "\n\nTablets:\n";
for (auto& d : g_pInputManager->m_vTabletPads) { for (auto const& d : g_pInputManager->m_vTabletPads) {
result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->parent.get(), d->parent ? d->parent->hlName : ""); result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->parent.get(), d->parent ? d->parent->hlName : "");
} }
for (auto& d : g_pInputManager->m_vTablets) { for (auto const& d : g_pInputManager->m_vTablets) {
result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y); result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y);
} }
for (auto& d : g_pInputManager->m_vTabletTools) { for (auto const& d : g_pInputManager->m_vTabletTools) {
result += std::format("\tTablet Tool at {:x}\n", (uintptr_t)d.get()); result += std::format("\tTablet Tool at {:x}\n", (uintptr_t)d.get());
} }
result += "\n\nTouch:\n"; result += "\n\nTouch:\n";
for (auto& d : g_pInputManager->m_vTouches) { for (auto const& d : g_pInputManager->m_vTouches) {
result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->hlName); result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->hlName);
} }
result += "\n\nSwitches:\n"; result += "\n\nSwitches:\n";
for (auto& d : g_pInputManager->m_lSwitches) { for (auto const& d : g_pInputManager->m_lSwitches) {
result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pDevice ? d.pDevice->getName() : ""); result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pDevice ? d.pDevice->getName() : "");
} }
} }
@ -712,21 +708,21 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
ret += "animations:\n"; ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) { for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second.overridden, ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second.overridden,
ac.second.internalBezier, ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle); ac.second.internalBezier, ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle);
} }
ret += "beziers:\n"; ret += "beziers:\n";
for (auto& bz : g_pAnimationManager->getAllBeziers()) { for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format("\n\tname: {}\n", bz.first); ret += std::format("\n\tname: {}\n", bz.first);
} }
} else { } else {
// json // json
ret += "[["; ret += "[[";
for (auto& ac : g_pConfigManager->getAnimationConfig()) { for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format(R"#( ret += std::format(R"#(
{{ {{
"name": "{}", "name": "{}",
@ -744,7 +740,7 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
ret += ",\n["; ret += ",\n[";
for (auto& bz : g_pAnimationManager->getAllBeziers()) { for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format(R"#( ret += std::format(R"#(
{{ {{
"name": "{}" "name": "{}"
@ -778,11 +774,11 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
std::string ret = ""; std::string ret = "";
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts(); const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS) for (auto const& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description); ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else { } else {
ret += "["; ret += "[";
for (auto& sh : SHORTCUTS) { for (auto const& sh : SHORTCUTS) {
ret += std::format(R"#( ret += std::format(R"#(
{{ {{
"name": "{}", "name": "{}",
@ -800,7 +796,7 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) { std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = ""; std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& kb : g_pKeybindManager->m_lKeybinds) { for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
ret += "bind"; ret += "bind";
if (kb.locked) if (kb.locked)
ret += "l"; ret += "l";
@ -821,7 +817,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
} else { } else {
// json // json
ret += "["; ret += "[";
for (auto& kb : g_pKeybindManager->m_lKeybinds) { for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
ret += std::format( ret += std::format(
R"#( R"#(
{{ {{
@ -857,37 +853,47 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' '); std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg + std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + ", commits: " + GIT_COMMITS + "\n\nflags: (if any)\n"; "Date: {}\n"
"Tag: {}, commits: {}\n"
"built against aquamarine {}\n\n\n",
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION);
#if (!defined(LEGACY_RENDERER) && !defined(ISDEBUG) && !defined(NO_XWAYLAND))
result += "no flags were set\n";
#else
result += "flags set:\n";
#ifdef LEGACY_RENDERER #ifdef LEGACY_RENDERER
result += "legacyrenderer\n"; result += "legacyrenderer\n";
#endif #endif
#ifndef ISDEBUG #ifdef ISDEBUG
result += "debug\n"; result += "debug\n";
#endif #endif
#ifdef NO_XWAYLAND #ifdef NO_XWAYLAND
result += "no xwayland\n"; result += "no xwayland\n";
#endif #endif
#endif
return result; return result;
} else { } else {
std::string result = std::format( std::string result = std::format(
R"#({{ R"#({{
"branch": "{}", "branch": "{}",
"commit": "{}", "commit": "{}",
"version": "{}",
"dirty": {}, "dirty": {},
"commit_message": "{}", "commit_message": "{}",
"commit_date": "{}", "commit_date": "{}",
"tag": "{}", "tag": "{}",
"commits": "{}", "commits": "{}",
"buildAquamarine": "{}",
"flags": [)#", "flags": [)#",
GIT_BRANCH, GIT_COMMIT_HASH, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS); GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
GIT_COMMITS, AQUAMARINE_VERSION);
#ifdef LEGACY_RENDERER #ifdef LEGACY_RENDERER
result += "\"legacyrenderer\","; result += "\"legacyrenderer\",";
#endif #endif
#ifndef ISDEBUG #ifdef ISDEBUG
result += "\"debug\","; result += "\"debug\",";
#endif #endif
#ifdef NO_XWAYLAND #ifdef NO_XWAYLAND
@ -935,11 +941,14 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request)
result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n"; result += "os-release: " + execAndGet("cat /etc/os-release") + "\n\n";
result += "plugins:\n"; result += "plugins:\n";
for (auto& pl : g_pPluginSystem->getAllPlugins()) { if (g_pPluginSystem) {
for (auto const& pl : g_pPluginSystem->getAllPlugins()) {
result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version); result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version);
} }
} else
result += "\tunknown: not runtime\n";
if (g_pHyprCtl->m_sCurrentRequestParams.sysInfoConfig) { if (g_pHyprCtl && g_pHyprCtl->m_sCurrentRequestParams.sysInfoConfig) {
result += "\n======Config-Start======\n"; result += "\n======Config-Start======\n";
result += g_pConfigManager->getConfigString(); result += g_pConfigManager->getConfigString();
result += "\n======Config-End========\n"; result += "\n======Config-End========\n";
@ -962,20 +971,34 @@ std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end())
return "Invalid dispatcher"; return "Invalid dispatcher";
DISPATCHER->second(DISPATCHARG); SDispatchResult res = DISPATCHER->second(DISPATCHARG);
Debug::log(LOG, "Hyprctl: dispatcher {} : {}", DISPATCHSTR, DISPATCHARG); Debug::log(LOG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
return "ok"; return res.success ? "ok" : res.error;
} }
std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) { std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// get rid of the keyword keyword // Find the first space to strip the keyword keyword
in = in.substr(in.find_first_of(' ') + 1); auto const firstSpacePos = in.find_first_of(' ');
if (firstSpacePos == std::string::npos) // Handle the case where there's no space found (invalid input)
return "Invalid input: no space found";
const auto COMMAND = in.substr(0, in.find_first_of(' ')); // Strip the keyword
in = in.substr(firstSpacePos + 1);
const auto VALUE = in.substr(in.find_first_of(' ') + 1); // Find the next space for the COMMAND and VALUE
auto const secondSpacePos = in.find_first_of(' ');
if (secondSpacePos == std::string::npos) // Handle the case where there's no second space (invalid input)
return "Invalid input: command and value not properly formatted";
// Extract COMMAND and VALUE
const auto COMMAND = in.substr(0, secondSpacePos);
const auto VALUE = in.substr(secondSpacePos + 1);
// If COMMAND is empty, handle accordingly
if (COMMAND.empty())
return "Invalid input: command is empty";
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE); std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
@ -1006,7 +1029,7 @@ std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// decorations will probably need a repaint // decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") { if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get()); g_pHyprRenderer->damageMonitor(m.get());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
} }
@ -1127,15 +1150,9 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
const auto KB = vars[1]; const auto KB = vars[1];
const auto CMD = vars[2]; const auto CMD = vars[2];
// get kb SP<IKeyboard> pKeyboard;
const auto PKEYBOARD = std::find_if(g_pInputManager->m_vKeyboards.begin(), g_pInputManager->m_vKeyboards.end(),
[&](const auto& other) { return other->hlName == g_pInputManager->deviceNameToInternalString(KB); });
if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found";
const auto KEEB = *PKEYBOARD;
auto updateKeyboard = [](const SP<IKeyboard> KEEB, const std::string& CMD) -> std::optional<std::string> {
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap); const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap);
xkb_layout_index_t activeLayout = 0; xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) { while (activeLayout < LAYOUTS) {
@ -1162,6 +1179,43 @@ std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string requ
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, requestedLayout); KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, requestedLayout);
} }
return std::nullopt;
};
if (KB == "main" || KB == "active" || KB == "current") {
for (auto const& k : g_pInputManager->m_vKeyboards) {
if (!k->active)
continue;
pKeyboard = k;
break;
}
} else if (KB == "all") {
std::string result = "";
for (auto const& k : g_pInputManager->m_vKeyboards) {
auto res = updateKeyboard(k, CMD);
if (res.has_value())
result += *res + "\n";
}
return result.empty() ? "ok" : result;
} else {
auto k = std::find_if(g_pInputManager->m_vKeyboards.begin(), g_pInputManager->m_vKeyboards.end(),
[&](const auto& other) { return other->hlName == g_pInputManager->deviceNameToInternalString(KB); });
if (k == g_pInputManager->m_vKeyboards.end())
return "device not found";
pKeyboard = *k;
}
if (!pKeyboard)
return "no device";
auto result = updateKeyboard(pKeyboard, CMD);
if (result.has_value())
return *result;
return "ok"; return "ok";
} }
@ -1286,7 +1340,7 @@ std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
g_pCompositor->focusWindow(PLASTWINDOW); g_pCompositor->focusWindow(PLASTWINDOW);
} }
for (auto& m : g_pCompositor->m_vMonitors) for (auto const& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
return "ok"; return "ok";
@ -1359,7 +1413,7 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
std::string result = ""; std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) { if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "["; result += "[";
for (auto& wd : PWINDOW->m_dWindowDecorations) { for (auto const& wd : PWINDOW->m_dWindowDecorations) {
result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},"; result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},";
} }
@ -1367,7 +1421,7 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
result += "]"; result += "]";
} else { } else {
result = +"Decoration\tPriority\n"; result = +"Decoration\tPriority\n";
for (auto& wd : PWINDOW->m_dWindowDecorations) { for (auto const& wd : PWINDOW->m_dWindowDecorations) {
result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n"; result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n";
} }
} }
@ -1386,7 +1440,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
bool added = false; bool added = false;
if (!vars[3].empty()) { if (!vars[3].empty()) {
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto const& m : g_pCompositor->m_vRealMonitors) {
if (m->szName == vars[3]) if (m->szName == vars[3])
return "Name already taken"; return "Name already taken";
} }
@ -1396,7 +1450,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
if (g_pCompositor->getMonitorFromName(vars[3])) if (g_pCompositor->getMonitorFromName(vars[3]))
return "A real monitor already uses that name."; return "A real monitor already uses that name.";
for (auto& impl : g_pCompositor->m_pAqBackend->getImplementations() | std::views::reverse) { for (auto const& impl : g_pCompositor->m_pAqBackend->getImplementations() | std::views::reverse) {
auto type = impl->type(); auto type = impl->type();
if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) { if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) {
@ -1464,7 +1518,7 @@ std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string request) {
return "no plugins loaded"; return "no plugins loaded";
std::string list = ""; std::string list = "";
for (auto& p : PLUGINS) { for (auto const& p : PLUGINS) {
list += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description); list += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description);
} }
@ -1561,6 +1615,29 @@ std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
return lockedStr; return lockedStr;
} }
std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) {
std::string json = "{";
const auto& DESCS = g_pConfigManager->getAllDescriptions();
for (const auto& d : DESCS) {
json += d.jsonify() + ",\n";
}
json.pop_back();
json.pop_back();
json += "}\n";
return json;
}
std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
std::string submap = g_pKeybindManager->getCurrentSubmap();
if (submap.empty())
submap = "default";
return format == FORMAT_JSON ? std::format("{{\"{}\"}}\n", escapeJSONStrings(submap)) : (submap + "\n");
}
CHyprCtl::CHyprCtl() { CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest}); registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest}); registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
@ -1581,6 +1658,8 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest}); registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest}); registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
@ -1657,7 +1736,7 @@ std::string CHyprCtl::getReply(std::string request) {
std::string result = ""; std::string result = "";
// parse exact cmds first, then non-exact. // parse exact cmds first, then non-exact.
for (auto& cmd : m_vCommands) { for (auto const& cmd : m_vCommands) {
if (!cmd->exact) if (!cmd->exact)
continue; continue;
@ -1668,7 +1747,7 @@ std::string CHyprCtl::getReply(std::string request) {
} }
if (result.empty()) if (result.empty())
for (auto& cmd : m_vCommands) { for (auto const& cmd : m_vCommands) {
if (cmd->exact) if (cmd->exact)
continue; continue;
@ -1699,7 +1778,7 @@ std::string CHyprCtl::getReply(std::string request) {
rd.blurFBDirty = true; rd.blurFBDirty = true;
} }
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get()); g_pHyprRenderer->damageMonitor(m.get());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
} }

View file

@ -5,6 +5,10 @@
#include "../helpers/MiscFunctions.hpp" #include "../helpers/MiscFunctions.hpp"
#include <functional> #include <functional>
// exposed for main.cpp
std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request);
std::string versionRequest(eHyprCtlOutputFormat format, std::string request);
class CHyprCtl { class CHyprCtl {
public: public:
CHyprCtl(); CHyprCtl();

View file

@ -7,8 +7,8 @@ CHyprDebugOverlay::CHyprDebugOverlay() {
m_pTexture = makeShared<CTexture>(); m_pTexture = makeShared<CTexture>();
} }
void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float µs) { void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float durationUs) {
m_dLastRenderTimes.push_back(µs / 1000.f); m_dLastRenderTimes.push_back(durationUs / 1000.f);
if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate) if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate)
m_dLastRenderTimes.pop_front(); m_dLastRenderTimes.pop_front();
@ -17,8 +17,8 @@ void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float µs) {
m_pMonitor = pMonitor; m_pMonitor = pMonitor;
} }
void CHyprMonitorDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float µs) { void CHyprMonitorDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float durationUs) {
m_dLastRenderTimesNoOverlay.push_back(µs / 1000.f); m_dLastRenderTimesNoOverlay.push_back(durationUs / 1000.f);
if (m_dLastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->refreshRate) if (m_dLastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->refreshRate)
m_dLastRenderTimesNoOverlay.pop_front(); m_dLastRenderTimesNoOverlay.pop_front();
@ -57,7 +57,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
float avgFrametime = 0; float avgFrametime = 0;
float maxFrametime = 0; float maxFrametime = 0;
float minFrametime = 9999; float minFrametime = 9999;
for (auto& ft : m_dLastFrametimes) { for (auto const& ft : m_dLastFrametimes) {
if (ft > maxFrametime) if (ft > maxFrametime)
maxFrametime = ft; maxFrametime = ft;
if (ft < minFrametime) if (ft < minFrametime)
@ -70,7 +70,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
float avgRenderTime = 0; float avgRenderTime = 0;
float maxRenderTime = 0; float maxRenderTime = 0;
float minRenderTime = 9999; float minRenderTime = 9999;
for (auto& rt : m_dLastRenderTimes) { for (auto const& rt : m_dLastRenderTimes) {
if (rt > maxRenderTime) if (rt > maxRenderTime)
maxRenderTime = rt; maxRenderTime = rt;
if (rt < minRenderTime) if (rt < minRenderTime)
@ -83,7 +83,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
float avgRenderTimeNoOverlay = 0; float avgRenderTimeNoOverlay = 0;
float maxRenderTimeNoOverlay = 0; float maxRenderTimeNoOverlay = 0;
float minRenderTimeNoOverlay = 9999; float minRenderTimeNoOverlay = 9999;
for (auto& rt : m_dLastRenderTimesNoOverlay) { for (auto const& rt : m_dLastRenderTimesNoOverlay) {
if (rt > maxRenderTimeNoOverlay) if (rt > maxRenderTimeNoOverlay)
maxRenderTimeNoOverlay = rt; maxRenderTimeNoOverlay = rt;
if (rt < minRenderTimeNoOverlay) if (rt < minRenderTimeNoOverlay)
@ -96,7 +96,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
float avgAnimMgrTick = 0; float avgAnimMgrTick = 0;
float maxAnimMgrTick = 0; float maxAnimMgrTick = 0;
float minAnimMgrTick = 9999; float minAnimMgrTick = 9999;
for (auto& at : m_dLastAnimationTicks) { for (auto const& at : m_dLastAnimationTicks) {
if (at > maxAnimMgrTick) if (at > maxAnimMgrTick)
maxAnimMgrTick = at; maxAnimMgrTick = at;
if (at < minAnimMgrTick) if (at < minAnimMgrTick)
@ -188,12 +188,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
return posY - offset; return posY - offset;
} }
void CHyprDebugOverlay::renderData(CMonitor* pMonitor, float µs) { void CHyprDebugOverlay::renderData(CMonitor* pMonitor, float durationUs) {
m_mMonitorOverlays[pMonitor].renderData(pMonitor, µs); m_mMonitorOverlays[pMonitor].renderData(pMonitor, durationUs);
} }
void CHyprDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float µs) { void CHyprDebugOverlay::renderDataNoOverlay(CMonitor* pMonitor, float durationUs) {
m_mMonitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, µs); m_mMonitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
} }
void CHyprDebugOverlay::frameData(CMonitor* pMonitor) { void CHyprDebugOverlay::frameData(CMonitor* pMonitor) {
@ -217,7 +217,7 @@ void CHyprDebugOverlay::draw() {
// draw the things // draw the things
int offsetY = 0; int offsetY = 0;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
offsetY += m_mMonitorOverlays[m.get()].draw(offsetY); offsetY += m_mMonitorOverlays[m.get()].draw(offsetY);
offsetY += 5; // for padding between mons offsetY += 5; // for padding between mons
} }

View file

@ -13,8 +13,8 @@ class CHyprMonitorDebugOverlay {
public: public:
int draw(int offset); int draw(int offset);
void renderData(CMonitor* pMonitor, float µs); void renderData(CMonitor* pMonitor, float durationUs);
void renderDataNoOverlay(CMonitor* pMonitor, float µs); void renderDataNoOverlay(CMonitor* pMonitor, float durationUs);
void frameData(CMonitor* pMonitor); void frameData(CMonitor* pMonitor);
private: private:
@ -33,8 +33,8 @@ class CHyprDebugOverlay {
public: public:
CHyprDebugOverlay(); CHyprDebugOverlay();
void draw(); void draw();
void renderData(CMonitor*, float µs); void renderData(CMonitor*, float durationUs);
void renderDataNoOverlay(CMonitor*, float µs); void renderDataNoOverlay(CMonitor*, float durationUs);
void frameData(CMonitor*); void frameData(CMonitor*);
private: private:

View file

@ -37,14 +37,14 @@ CHyprNotificationOverlay::~CHyprNotificationOverlay() {
void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon, const float fontSize) { void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon, const float fontSize) {
const auto PNOTIF = m_dNotifications.emplace_back(std::make_unique<SNotification>()).get(); const auto PNOTIF = m_dNotifications.emplace_back(std::make_unique<SNotification>()).get();
PNOTIF->text = text; PNOTIF->text = icon != eIcons::ICON_NONE ? " " + text /* tiny bit of padding otherwise icon touches text */ : text;
PNOTIF->color = color == CColor(0) ? ICONS_COLORS[icon] : color; PNOTIF->color = color == CColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->started.reset(); PNOTIF->started.reset();
PNOTIF->timeMs = timeMs; PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon; PNOTIF->icon = icon;
PNOTIF->fontSize = fontSize; PNOTIF->fontSize = fontSize;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get()); g_pCompositor->scheduleFrameForMonitor(m.get());
} }
} }
@ -87,7 +87,7 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
const auto iconBackendID = iconBackendFromLayout(layout); const auto iconBackendID = iconBackendFromLayout(layout);
const auto PBEZIER = g_pAnimationManager->getBezier("default"); const auto PBEZIER = g_pAnimationManager->getBezier("default");
for (auto& notif : m_dNotifications) { for (auto const& notif : m_dNotifications) {
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD; const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40); const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);

View file

@ -4,11 +4,14 @@
#include "RollingLogFollow.hpp" #include "RollingLogFollow.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <print>
#include <fcntl.h>
void Debug::init(const std::string& IS) { void Debug::init(const std::string& IS) {
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log"); logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
logOfs.open(logFile, std::ios::out | std::ios::app); logOfs.open(logFile, std::ios::out | std::ios::app);
auto handle = logOfs.native_handle();
fcntl(handle, F_SETFD, FD_CLOEXEC);
} }
void Debug::close() { void Debug::close() {
@ -66,5 +69,5 @@ void Debug::log(LogLevel level, std::string str) {
// log it to the stdout too. // log it to the stdout too.
if (!disableStdout) if (!disableStdout)
std::cout << ((coloredLogs && !**coloredLogs) ? str : coloredStr) << "\n"; std::println("{}", ((coloredLogs && !**coloredLogs) ? str : coloredStr));
} }

View file

@ -55,7 +55,8 @@ namespace Debug {
// 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
const auto zt = std::chrono::zoned_time{std::chrono::current_zone(), std::chrono::system_clock::now()}; static auto current_zone = std::chrono::current_zone();
const auto zt = std::chrono::zoned_time{current_zone, std::chrono::system_clock::now()};
const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor<std::chrono::days>(zt.get_local_time())}; const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor<std::chrono::days>(zt.get_local_time())};
#else #else
// TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready // TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready
@ -72,6 +73,5 @@ namespace Debug {
logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
log(level, logMsg); log(level, logMsg);
logMutex.unlock();
} }
}; };

View file

@ -1,6 +1,5 @@
#include "includes.hpp" #include "includes.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "helpers/WLListener.hpp"
#include "helpers/Color.hpp" #include "helpers/Color.hpp"
#include "macros.hpp" #include "macros.hpp"
#include "desktop/DesktopTypes.hpp" #include "desktop/DesktopTypes.hpp"

View file

@ -71,6 +71,12 @@ CLayerSurface::~CLayerSurface() {
surface->unassign(); surface->unassign();
g_pHyprRenderer->makeEGLCurrent(); g_pHyprRenderer->makeEGLCurrent();
std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); }); std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); });
for (auto const& mon : g_pCompositor->m_vRealMonitors) {
for (auto& lsl : mon->m_aLayerSurfaceLayers) {
std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; });
}
}
} }
void CLayerSurface::onDestroy() { void CLayerSurface::onDestroy() {
@ -360,7 +366,7 @@ void CLayerSurface::applyRules() {
xray = -1; xray = -1;
animationStyle.reset(); animationStyle.reset();
for (auto& rule : g_pConfigManager->getMatchingRules(self.lock())) { for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) {
if (rule.rule == "noanim") if (rule.rule == "noanim")
noAnimations = true; noAnimations = true;
else if (rule.rule == "blur") else if (rule.rule == "blur")
@ -388,6 +394,11 @@ void CLayerSurface::applyRules() {
} else if (rule.rule.starts_with("animation")) { } else if (rule.rule.starts_with("animation")) {
CVarList vars{rule.rule, 2, 's'}; CVarList vars{rule.rule, 2, 's'};
animationStyle = vars[1]; animationStyle = vars[1];
} else if (rule.rule.starts_with("order")) {
CVarList vars{rule.rule, 2, 's'};
try {
order = std::stoi(vars[1]);
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
} }
} }
} }

View file

@ -42,7 +42,7 @@ class CLayerSurface {
bool mapped = false; bool mapped = false;
uint32_t layer = 0; uint32_t layer = 0;
int monitorID = -1; MONITORID monitorID = -1;
bool fadingOut = false; bool fadingOut = false;
bool readyToDelete = false; bool readyToDelete = false;
@ -51,10 +51,11 @@ class CLayerSurface {
bool forceBlur = false; bool forceBlur = false;
bool forceBlurPopups = false; bool forceBlurPopups = false;
int xray = -1; int64_t xray = -1;
bool ignoreAlpha = false; bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f; float ignoreAlphaValue = 0.f;
bool dimAround = false; bool dimAround = false;
int64_t order = 0;
std::optional<std::string> animationStyle; std::optional<std::string> animationStyle;

View file

@ -202,6 +202,13 @@ void CPopup::reposition() {
m_pResource->applyPositioning(box, COORDS); m_pResource->applyPositioning(box, COORDS);
} }
SP<CWLSurface> CPopup::getT1Owner() {
if (m_pWindowOwner)
return m_pWindowOwner->m_pWLSurface;
else
return m_pLayerOwner->surface;
}
Vector2D CPopup::coordsRelativeToParent() { Vector2D CPopup::coordsRelativeToParent() {
Vector2D offset; Vector2D offset;
@ -251,7 +258,7 @@ void CPopup::recheckTree() {
void CPopup::recheckChildrenRecursive() { void CPopup::recheckChildrenRecursive() {
auto cpy = m_vChildren; auto cpy = m_vChildren;
for (auto& c : cpy) { for (auto const& c : cpy) {
c->onCommit(true); c->onCommit(true);
c->recheckChildrenRecursive(); c->recheckChildrenRecursive();
} }
@ -282,14 +289,14 @@ bool CPopup::visible() {
} }
void CPopup::bfHelper(std::vector<CPopup*> nodes, std::function<void(CPopup*, void*)> fn, void* data) { void CPopup::bfHelper(std::vector<CPopup*> nodes, std::function<void(CPopup*, void*)> fn, void* data) {
for (auto& n : nodes) { for (auto const& n : nodes) {
fn(n, data); fn(n, data);
} }
std::vector<CPopup*> nodes2; std::vector<CPopup*> nodes2;
for (auto& n : nodes) { for (auto const& n : nodes) {
for (auto& c : n->m_vChildren) { for (auto const& c : n->m_vChildren) {
nodes2.push_back(c.get()); nodes2.push_back(c.get());
} }
} }
@ -308,7 +315,7 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<CPopup*> popups; std::vector<CPopup*> popups;
breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups); breadthfirst([](CPopup* popup, void* data) { ((std::vector<CPopup*>*)data)->push_back(popup); }, &popups);
for (auto& p : popups | std::views::reverse) { for (auto const& p : popups | std::views::reverse) {
if (!p->m_pResource) if (!p->m_pResource)
continue; continue;

View file

@ -18,6 +18,7 @@ class CPopup {
~CPopup(); ~CPopup();
SP<CWLSurface> getT1Owner();
Vector2D coordsRelativeToParent(); Vector2D coordsRelativeToParent();
Vector2D coordsGlobal(); Vector2D coordsGlobal();

View file

@ -30,13 +30,7 @@ CSubsurface::CSubsurface(SP<CWLSubsurfaceResource> pSubsurface, CPopup* pOwner)
} }
CSubsurface::~CSubsurface() { CSubsurface::~CSubsurface() {
hyprListener_newSubsurface.removeCallback(); ;
if (!m_pSubsurface)
return;
hyprListener_commitSubsurface.removeCallback();
hyprListener_destroySubsurface.removeCallback();
} }
void CSubsurface::initSignals() { void CSubsurface::initSignals() {
@ -65,7 +59,7 @@ void CSubsurface::checkSiblingDamage() {
const double SCALE = m_pWindowParent.lock() && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0; const double SCALE = m_pWindowParent.lock() && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0;
for (auto& n : m_pParent->m_vChildren) { for (auto const& n : m_pParent->m_vChildren) {
if (n.get() == this) if (n.get() == this)
continue; continue;
@ -75,7 +69,7 @@ void CSubsurface::checkSiblingDamage() {
} }
void CSubsurface::recheckDamageForSubsurfaces() { void CSubsurface::recheckDamageForSubsurfaces() {
for (auto& n : m_vChildren) { for (auto const& n : m_vChildren) {
const auto COORDS = n->coordsGlobal(); const auto COORDS = n->coordsGlobal();
g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y); g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y);
} }
@ -183,7 +177,7 @@ Vector2D CSubsurface::coordsGlobal() {
} }
void CSubsurface::initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface) { void CSubsurface::initExistingSubsurfaces(SP<CWLSurfaceResource> pSurface) {
for (auto& s : pSurface->subsurfaces) { for (auto const& s : pSurface->subsurfaces) {
if (!s || s->surface->hlSurface /* already assigned */) if (!s || s->surface->hlSurface /* already assigned */)
continue; continue;
onNewSubsurface(s.lock()); onNewSubsurface(s.lock());

View file

@ -35,10 +35,6 @@ class CSubsurface {
void recheckDamageForSubsurfaces(); void recheckDamageForSubsurfaces();
private: private:
DYNLISTENER(destroySubsurface);
DYNLISTENER(commitSubsurface);
DYNLISTENER(newSubsurface);
struct { struct {
CHyprSignalListener destroySubsurface; CHyprSignalListener destroySubsurface;
CHyprSignalListener commitSubsurface; CHyprSignalListener commitSubsurface;

View file

@ -62,7 +62,7 @@ bool CWLSurface::small() const {
const auto O = m_pWindowOwner.lock(); const auto O = m_pWindowOwner.lock();
return O->m_vReportedSize.x > m_pResource->current.bufferSize.x + 1 || O->m_vReportedSize.y > m_pResource->current.bufferSize.y + 1; return O->m_vReportedSize.x > m_pResource->current.size.x + 1 || O->m_vReportedSize.y > m_pResource->current.size.y + 1;
} }
Vector2D CWLSurface::correctSmallVec() const { Vector2D CWLSurface::correctSmallVec() const {

View file

@ -21,7 +21,6 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
pWindow->m_pSelf = pWindow; pWindow->m_pSelf = pWindow;
pWindow->m_bIsX11 = true; pWindow->m_bIsX11 = true;
pWindow->m_iX11Type = surface->overrideRedirect ? 2 : 1;
pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@ -31,6 +30,7 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fMovingToWorkspaceAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow)); pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow)); pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow));
@ -52,6 +52,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_fActiveInactiveAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); pWindow->m_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_fDimPercent.create(g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->m_fMovingToWorkspaceAlpha.create(g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE);
pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow)); pWindow->addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(pWindow));
pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow)); pWindow->addWindowDeco(std::make_unique<CHyprBorderDecoration>(pWindow));
@ -252,7 +253,7 @@ void CWindow::updateWindowDecos() {
if (!m_bIsMapped || isHidden()) if (!m_bIsMapped || isHidden())
return; return;
for (auto& wd : m_vDecosToRemove) { for (auto const& 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()); g_pDecorationPositioner->uncacheDecoration(it->get());
@ -270,11 +271,11 @@ void CWindow::updateWindowDecos() {
// make a copy because updateWindow can remove decos. // make a copy because updateWindow can remove decos.
std::vector<IHyprWindowDecoration*> decos; std::vector<IHyprWindowDecoration*> decos;
for (auto& wd : m_dWindowDecorations) { for (auto const& wd : m_dWindowDecorations) {
decos.push_back(wd.get()); decos.push_back(wd.get());
} }
for (auto& wd : decos) { for (auto const& wd : decos) {
if (std::find_if(m_dWindowDecorations.begin(), m_dWindowDecorations.end(), [wd](const auto& other) { return other.get() == wd; }) == m_dWindowDecorations.end()) if (std::find_if(m_dWindowDecorations.begin(), m_dWindowDecorations.end(), [wd](const auto& other) { return other.get() == wd; }) == m_dWindowDecorations.end())
continue; continue;
wd->updateWindow(m_pSelf.lock()); wd->updateWindow(m_pSelf.lock());
@ -296,7 +297,7 @@ void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
} }
void CWindow::uncacheWindowDecos() { void CWindow::uncacheWindowDecos() {
for (auto& wd : m_dWindowDecorations) { for (auto const& wd : m_dWindowDecorations) {
g_pDecorationPositioner->uncacheDecoration(wd.get()); g_pDecorationPositioner->uncacheDecoration(wd.get());
} }
} }
@ -305,7 +306,7 @@ bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoor
if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords)) if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords))
return false; return false;
for (auto& wd : m_dWindowDecorations) { for (auto const& wd : m_dWindowDecorations) {
if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT)) if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
continue; continue;
@ -337,7 +338,7 @@ pid_t CWindow::getPID() {
} }
IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) { IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) {
for (auto& wd : m_dWindowDecorations) { for (auto const& wd : m_dWindowDecorations) {
if (wd->getDecorationType() == type) if (wd->getDecorationType() == type)
return wd.get(); return wd.get();
} }
@ -407,6 +408,11 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
const auto OLDWORKSPACE = m_pWorkspace; const auto OLDWORKSPACE = m_pWorkspace;
m_iMonitorMovedFrom = OLDWORKSPACE ? OLDWORKSPACE->m_iMonitorID : -1;
m_fMovingToWorkspaceAlpha.setValueAndWarp(1.F);
m_fMovingToWorkspaceAlpha = 0.F;
m_fMovingToWorkspaceAlpha.setCallbackOnEnd([this](void* thisptr) { m_iMonitorMovedFrom = -1; });
m_pWorkspace = pWorkspace; m_pWorkspace = pWorkspace;
setAnimationsToMove(); setAnimationsToMove();
@ -446,15 +452,21 @@ PHLWINDOW CWindow::X11TransientFor() {
return nullptr; return nullptr;
auto s = m_pXWaylandSurface->parent; auto s = m_pXWaylandSurface->parent;
auto oldParent = s; std::vector<SP<CXWaylandSurface>> visited;
while (s) { while (s) {
// break cyclic loop of m_pXWaylandSurface being parent of itself, #TODO reject this from even being created? // break loops. Some X apps make them, and it seems like it's valid behavior?!?!?!
if (!s->parent || s->parent == oldParent) // TODO: we should reject loops being created in the first place.
if (std::find(visited.begin(), visited.end(), s) != visited.end())
break; break;
visited.emplace_back(s.lock());
s = s->parent; s = s->parent;
} }
for (auto& w : g_pCompositor->m_vWindows) { if (s == m_pXWaylandSurface)
return nullptr; // dead-ass circle
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pXWaylandSurface != s) if (w->m_pXWaylandSurface != s)
continue; continue;
return w; return w;
@ -464,7 +476,7 @@ PHLWINDOW CWindow::X11TransientFor() {
} }
void CWindow::removeDecorationByType(eDecorationType type) { void CWindow::removeDecorationByType(eDecorationType type) {
for (auto& wd : m_dWindowDecorations) { for (auto const& wd : m_dWindowDecorations) {
if (wd->getDecorationType() == type) if (wd->getDecorationType() == type)
m_vDecosToRemove.push_back(wd.get()); m_vDecosToRemove.push_back(wd.get());
} }
@ -502,6 +514,7 @@ void CWindow::onUnmap() {
m_fAlpha.setCallbackOnEnd(unregisterVar); m_fAlpha.setCallbackOnEnd(unregisterVar);
m_cRealShadowColor.setCallbackOnEnd(unregisterVar); m_cRealShadowColor.setCallbackOnEnd(unregisterVar);
m_fDimPercent.setCallbackOnEnd(unregisterVar); m_fDimPercent.setCallbackOnEnd(unregisterVar);
m_fMovingToWorkspaceAlpha.setCallbackOnEnd(unregisterVar);
m_vRealSize.setCallbackOnBegin(nullptr); m_vRealSize.setCallbackOnBegin(nullptr);
@ -542,6 +555,7 @@ void CWindow::onMap() {
m_fAlpha.resetAllCallbacks(); m_fAlpha.resetAllCallbacks();
m_cRealShadowColor.resetAllCallbacks(); m_cRealShadowColor.resetAllCallbacks();
m_fDimPercent.resetAllCallbacks(); m_fDimPercent.resetAllCallbacks();
m_fMovingToWorkspaceAlpha.resetAllCallbacks();
m_vRealPosition.registerVar(); m_vRealPosition.registerVar();
m_vRealSize.registerVar(); m_vRealSize.registerVar();
@ -551,6 +565,7 @@ void CWindow::onMap() {
m_fAlpha.registerVar(); m_fAlpha.registerVar();
m_cRealShadowColor.registerVar(); m_cRealShadowColor.registerVar();
m_fDimPercent.registerVar(); m_fDimPercent.registerVar();
m_fMovingToWorkspaceAlpha.registerVar();
m_fBorderAngleAnimationProgress.setCallbackOnEnd([&](void* ptr) { onBorderAngleAnimEnd(ptr); }, false); m_fBorderAngleAnimationProgress.setCallbackOnEnd([&](void* ptr) { onBorderAngleAnimEnd(ptr); }, false);
@ -617,7 +632,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
int opacityIDX = 0; int opacityIDX = 0;
for (auto& r : vars) { for (auto const& r : vars) {
if (r == "opacity") if (r == "opacity")
continue; continue;
@ -666,7 +681,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
return; return;
} }
for (auto& token : colorsAndAngles) { for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients // The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) { if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
@ -727,9 +742,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} }
m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority); m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority);
m_vRealSize = clampWindowSize(std::nullopt, m_sWindowData.maxSize.value());
Vector2D(std::min((double)m_sWindowData.maxSize.value().x, m_vRealSize.goal().x), std::min((double)m_sWindowData.maxSize.value().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.starts_with("minsize")) { } else if (r.szRule.starts_with("minsize")) {
try { try {
@ -742,12 +756,14 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
} }
m_sWindowData.minSize = CWindowOverridableVar(VEC, priority); m_sWindowData.minSize = CWindowOverridableVar(VEC, priority);
m_vRealSize = clampWindowSize(m_sWindowData.minSize.value(), std::nullopt);
Vector2D(std::max((double)m_sWindowData.minSize.value().x, m_vRealSize.goal().x), std::max((double)m_sWindowData.minSize.value().y, m_vRealSize.goal().y));
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal());
if (m_sGroupData.pNextWindow.expired()) if (m_sGroupData.pNextWindow.expired())
setHidden(false); setHidden(false);
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r.szRule, e.what()); } } catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule == "renderunfocused") {
m_sWindowData.renderUnfocused = CWindowOverridableVar(true, priority);
g_pHyprRenderer->addWindowToRenderUnfocused(m_pSelf.lock());
} }
} }
@ -765,12 +781,14 @@ void CWindow::updateDynamicRules() {
m_sWindowData.activeBorderColor.unset(PRIORITY_WINDOW_RULE); m_sWindowData.activeBorderColor.unset(PRIORITY_WINDOW_RULE);
m_sWindowData.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE); m_sWindowData.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE);
m_sWindowData.renderUnfocused.unset(PRIORITY_WINDOW_RULE);
m_eIdleInhibitMode = IDLEINHIBIT_NONE; m_eIdleInhibitMode = IDLEINHIBIT_NONE;
m_tags.removeDynamicTags(); m_tags.removeDynamicTags();
m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock()); m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock());
for (auto& r : m_vMatchedRules) { for (auto const& r : m_vMatchedRules) {
applyDynamicRule(r); applyDynamicRule(r);
} }
@ -881,7 +899,7 @@ void CWindow::destroyGroup() {
addresses += std::format("{:x},", (uintptr_t)curr.get()); addresses += std::format("{:x},", (uintptr_t)curr.get());
} while (curr.get() != this); } while (curr.get() != this);
for (auto& w : members) { for (auto const& w : members) {
if (w->m_sGroupData.head) if (w->m_sGroupData.head)
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr); g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr);
w->m_sGroupData.head = false; w->m_sGroupData.head = false;
@ -889,7 +907,7 @@ void CWindow::destroyGroup() {
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked; const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked;
g_pKeybindManager->m_bGroupsLocked = true; g_pKeybindManager->m_bGroupsLocked = true;
for (auto& w : members) { for (auto const& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w); g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
w->updateWindowDecos(); w->updateWindowDecos();
} }
@ -937,12 +955,16 @@ int CWindow::getGroupSize() {
} }
bool CWindow::canBeGroupedInto(PHLWINDOW pWindow) { bool CWindow::canBeGroupedInto(PHLWINDOW pWindow) {
static auto ALLOWGROUPMERGE = CConfigValue<Hyprlang::INT>("group:merge_groups_on_drag");
bool isGroup = m_sGroupData.pNextWindow;
bool disallowDragIntoGroup = g_pInputManager->m_bWasDraggingWindow && isGroup && !bool(*ALLOWGROUPMERGE);
return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged
&& ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or && ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or
|| (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked || (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked
&& !(m_sGroupData.pNextWindow.lock() && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group && !(m_sGroupData.pNextWindow.lock() && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !m_sGroupData.deny // source is not denied entry && !m_sGroupData.deny // source is not denied entry
&& !(m_eGroupRules & GROUP_BARRED && m_bFirstMap); // group rule doesn't prevent adding window && !(m_eGroupRules & GROUP_BARRED && m_bFirstMap) // group rule doesn't prevent adding window
&& !disallowDragIntoGroup; // config allows groups to be merged
} }
PHLWINDOW CWindow::getGroupWindowByIndex(int index) { PHLWINDOW CWindow::getGroupWindowByIndex(int index) {
@ -973,7 +995,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
const auto PCURRENT = getGroupCurrent(); const auto PCURRENT = getGroupCurrent();
const bool FULLSCREEN = PCURRENT->isFullscreen(); const bool FULLSCREEN = PCURRENT->isFullscreen();
const auto WORKSPACE = PCURRENT->m_pWorkspace; const auto WORKSPACE = PCURRENT->m_pWorkspace;
const auto MODE = PCURRENT->m_sFullscreenState.client; const auto MODE = PCURRENT->m_sFullscreenState.internal;
const auto PWINDOWSIZE = PCURRENT->m_vRealSize.goal(); const auto PWINDOWSIZE = PCURRENT->m_vRealSize.goal();
const auto PWINDOWPOS = PCURRENT->m_vRealPosition.goal(); const auto PWINDOWPOS = PCURRENT->m_vRealPosition.goal();
@ -1235,6 +1257,16 @@ int CWindow::surfacesCount() {
return no; return no;
} }
void CWindow::clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize) {
const Vector2D REALSIZE = m_vRealSize.goal();
const Vector2D NEWSIZE = REALSIZE.clamp(minSize.value_or(Vector2D{0.f, 0.f}), maxSize.value_or(Vector2D{INFINITY, INFINITY}));
const Vector2D DELTA = REALSIZE - NEWSIZE;
m_vRealPosition = m_vRealPosition.goal() + DELTA / 2.0;
m_vRealSize = NEWSIZE;
g_pXWaylandManager->setWindowSize(m_pSelf.lock(), NEWSIZE);
}
bool CWindow::isFullscreen() { bool CWindow::isFullscreen() {
return m_sFullscreenState.internal != FSMODE_NONE; return m_sFullscreenState.internal != FSMODE_NONE;
} }
@ -1243,7 +1275,7 @@ bool CWindow::isEffectiveInternalFSMode(const eFullscreenMode MODE) {
return (eFullscreenMode)std::bit_floor((uint8_t)m_sFullscreenState.internal) == MODE; return (eFullscreenMode)std::bit_floor((uint8_t)m_sFullscreenState.internal) == MODE;
} }
int CWindow::workspaceID() { WORKSPACEID CWindow::workspaceID() {
return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace; return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace;
} }
@ -1282,7 +1314,7 @@ std::unordered_map<std::string, std::string> CWindow::getEnv() {
CVarList envs(std::string{buffer.data(), buffer.size() - 1}, 0, '\n', true); CVarList envs(std::string{buffer.data(), buffer.size() - 1}, 0, '\n', true);
for (auto& e : envs) { for (auto const& e : envs) {
if (!e.contains('=')) if (!e.contains('='))
continue; continue;
@ -1307,6 +1339,11 @@ void CWindow::activate(bool force) {
if (!force && (!m_sWindowData.focusOnActivate.valueOr(*PFOCUSONACTIVATE) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE))) if (!force && (!m_sWindowData.focusOnActivate.valueOr(*PFOCUSONACTIVATE) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE)))
return; return;
if (!m_bIsMapped) {
Debug::log(LOG, "Ignoring CWindow::activate focus/warp, window is not mapped yet.");
return;
}
if (m_bIsFloating) if (m_bIsFloating)
g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true); g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true);
@ -1511,7 +1548,7 @@ PHLWINDOW CWindow::getSwallower() {
if (!currentPid) if (!currentPid)
break; break;
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->isHidden()) if (!w->m_bIsMapped || w->isHidden())
continue; continue;
@ -1536,7 +1573,7 @@ PHLWINDOW CWindow::getSwallower() {
return candidates.at(0); return candidates.at(0);
// walk up the focus history and find the last focused // walk up the focus history and find the last focused
for (auto& w : g_pCompositor->m_vWindowFocusHistory) { for (auto const& w : g_pCompositor->m_vWindowFocusHistory) {
if (!w) if (!w)
continue; continue;
@ -1556,3 +1593,11 @@ void CWindow::unsetWindowData(eOverridePriority priority) {
element.second(m_pSelf.lock())->unset(priority); element.second(m_pSelf.lock())->unset(priority);
} }
} }
bool CWindow::isX11OverrideRedirect() {
return m_pXWaylandSurface && m_pXWaylandSurface->overrideRedirect;
}
bool CWindow::isModal() {
return (m_pXWaylandSurface && m_pXWaylandSurface->modal);
}

View file

@ -174,6 +174,7 @@ struct SWindowData {
CWindowOverridableVar<bool> syncFullscreen = true; CWindowOverridableVar<bool> syncFullscreen = true;
CWindowOverridableVar<bool> tearing = false; CWindowOverridableVar<bool> tearing = false;
CWindowOverridableVar<bool> xray = false; CWindowOverridableVar<bool> xray = false;
CWindowOverridableVar<bool> renderUnfocused = false;
CWindowOverridableVar<int> rounding; CWindowOverridableVar<int> rounding;
CWindowOverridableVar<int> borderSize; CWindowOverridableVar<int> borderSize;
@ -201,6 +202,7 @@ struct SWindowRule {
int bFullscreen = -1; int bFullscreen = -1;
int bPinned = -1; int bPinned = -1;
int bFocus = -1; int bFocus = -1;
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any std::string szWorkspace = ""; // empty means any
}; };
@ -270,7 +272,7 @@ class CWindow {
bool m_bDraggingTiled = false; // for dragging around tiled windows bool m_bDraggingTiled = false; // for dragging around tiled windows
bool m_bWasMaximized = false; bool m_bWasMaximized = false;
sFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE}; sFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
uint64_t m_iMonitorID = -1; MONITORID m_iMonitorID = -1;
std::string m_szTitle = ""; std::string m_szTitle = "";
std::string m_szClass = ""; std::string m_szClass = "";
std::string m_szInitialTitle = ""; std::string m_szInitialTitle = "";
@ -287,8 +289,6 @@ class CWindow {
// XWayland stuff // XWayland stuff
bool m_bIsX11 = false; bool m_bIsX11 = false;
PHLWINDOWREF m_pX11Parent; PHLWINDOWREF m_pX11Parent;
uint64_t m_iX11Type = 0;
bool m_bIsModal = false;
bool m_bX11DoesntWantBorders = false; bool m_bX11DoesntWantBorders = false;
bool m_bX11ShouldntFocus = false; bool m_bX11ShouldntFocus = false;
float m_fX11SurfaceScaledBy = 1.f; float m_fX11SurfaceScaledBy = 1.f;
@ -351,6 +351,10 @@ class CWindow {
// animated tint // animated tint
CAnimatedVariable<float> m_fDimPercent; CAnimatedVariable<float> m_fDimPercent;
// animate moving to an invisible workspace
int m_iMonitorMovedFrom = -1; // -1 means not moving
CAnimatedVariable<float> m_fMovingToWorkspaceAlpha;
// swallowing // swallowing
PHLWINDOWREF m_pSwallowed; PHLWINDOWREF m_pSwallowed;
@ -358,8 +362,8 @@ class CWindow {
bool m_bStayFocused = false; bool m_bStayFocused = false;
// for toplevel monitor events // for toplevel monitor events
uint64_t m_iLastToplevelMonitorID = -1; MONITORID m_iLastToplevelMonitorID = -1;
uint64_t m_iLastSurfaceMonitorID = -1; MONITORID m_iLastSurfaceMonitorID = -1;
// for idle inhibiting windows // for idle inhibiting windows
eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE; eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE;
@ -421,10 +425,11 @@ class CWindow {
bool canBeTorn(); bool canBeTorn();
void setSuspended(bool suspend); void setSuspended(bool suspend);
bool visibleOnMonitor(CMonitor* pMonitor); bool visibleOnMonitor(CMonitor* pMonitor);
int workspaceID(); WORKSPACEID workspaceID();
bool onSpecialWorkspace(); bool onSpecialWorkspace();
void activate(bool force = false); void activate(bool force = false);
int surfacesCount(); int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen(); bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode); bool isEffectiveInternalFSMode(const eFullscreenMode);
@ -463,6 +468,8 @@ class CWindow {
void warpCursor(); void warpCursor();
PHLWINDOW getSwallower(); PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority); void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
// listeners // listeners
void onAck(uint32_t serial); void onAck(uint32_t serial);
@ -492,7 +499,7 @@ class CWindow {
// For hidden windows and stuff // For hidden windows and stuff
bool m_bHidden = false; bool m_bHidden = false;
bool m_bSuspended = false; bool m_bSuspended = false;
int m_iLastWorkspace = WORKSPACE_INVALID; WORKSPACEID m_iLastWorkspace = WORKSPACE_INVALID;
}; };
inline bool valid(PHLWINDOW w) { inline bool valid(PHLWINDOW w) {

View file

@ -5,27 +5,28 @@
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special, bool isEmtpy) { PHLWORKSPACE CWorkspace::create(WORKSPACEID id, MONITORID monitorID, std::string name, bool special, bool isEmpty) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmtpy); PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmpty);
workspace->init(workspace); workspace->init(workspace);
return workspace; return workspace;
} }
CWorkspace::CWorkspace(int id, int monitorID, std::string name, bool special, bool isEmtpy) { CWorkspace::CWorkspace(WORKSPACEID id, MONITORID monitorID, std::string name, bool special, bool isEmpty) {
m_iMonitorID = monitorID; m_iMonitorID = monitorID;
m_iID = id; m_iID = id;
m_szName = name; m_szName = name;
m_bIsSpecialWorkspace = special; m_bIsSpecialWorkspace = special;
m_bWasCreatedEmtpy = isEmtpy; m_bWasCreatedEmpty = isEmpty;
} }
void CWorkspace::init(PHLWORKSPACE self) { void CWorkspace::init(PHLWORKSPACE self) {
m_pSelf = self; m_pSelf = self;
m_vRenderOffset.create(m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"), m_vRenderOffset.create(m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") :
g_pConfigManager->getAnimationPropertyConfig("workspacesIn"),
self, AVARDAMAGE_ENTIRE); self, AVARDAMAGE_ENTIRE);
m_fAlpha.create(AVARTYPE_FLOAT, m_fAlpha.create(AVARTYPE_FLOAT,
m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspace") : g_pConfigManager->getAnimationPropertyConfig("workspaces"), self, m_bIsSpecialWorkspace ? g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn") : g_pConfigManager->getAnimationPropertyConfig("workspacesIn"), self,
AVARDAMAGE_ENTIRE); AVARDAMAGE_ENTIRE);
m_fAlpha.setValueAndWarp(1.f); m_fAlpha.setValueAndWarp(1.f);
@ -48,9 +49,9 @@ void CWorkspace::init(PHLWORKSPACE self) {
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self); const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
m_bPersistent = WORKSPACERULE.isPersistent; m_bPersistent = WORKSPACERULE.isPersistent;
if (self->m_bWasCreatedEmtpy) if (self->m_bWasCreatedEmpty)
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd) if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
g_pKeybindManager->spawn(*cmd); g_pKeybindManager->spawnWithRules(*cmd, self);
g_pEventManager->postEvent({"createworkspace", m_szName}); g_pEventManager->postEvent({"createworkspace", m_szName});
g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)}); g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)});
@ -81,12 +82,19 @@ CWorkspace::~CWorkspace() {
} }
void CWorkspace::startAnim(bool in, bool left, bool instant) { void CWorkspace::startAnim(bool in, bool left, bool instant) {
if (!instant) {
const std::string ANIMNAME = std::format("{}{}", m_bIsSpecialWorkspace ? "specialWorkspace" : "workspaces", in ? "In" : "Out");
m_fAlpha.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
m_vRenderOffset.m_pConfig = g_pConfigManager->getAnimationPropertyConfig(ANIMNAME);
}
const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle; const auto ANIMSTYLE = m_fAlpha.m_pConfig->pValues->internalStyle;
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces"); static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
// set floating windows offset callbacks // set floating windows offset callbacks
m_vRenderOffset.setUpdateCallback([&](void*) { m_vRenderOffset.setUpdateCallback([&](void*) {
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (!validMapped(w) || w->workspaceID() != m_iID) if (!validMapped(w) || w->workspaceID() != m_iID)
continue; continue;
@ -190,7 +198,7 @@ void CWorkspace::setActive(bool on) {
; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40
} }
void CWorkspace::moveToMonitor(const int& id) { void CWorkspace::moveToMonitor(const MONITORID& id) {
; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40
} }
@ -275,7 +283,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
i = std::min(NEXTSPACE, std::string::npos - 1); i = std::min(NEXTSPACE, std::string::npos - 1);
if (cur == 'r') { if (cur == 'r') {
int from = 0, to = 0; WORKSPACEID from = 0, to = 0;
if (!prop.starts_with("r[") || !prop.ends_with("]")) { if (!prop.starts_with("r[") || !prop.ends_with("]")) {
Debug::log(LOG, "Invalid selector {}", selector); Debug::log(LOG, "Invalid selector {}", selector);
return false; return false;
@ -365,7 +373,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
} }
if (cur == 'w') { if (cur == 'w') {
int from = 0, to = 0; WORKSPACEID from = 0, to = 0;
if (!prop.starts_with("w[") || !prop.ends_with("]")) { if (!prop.starts_with("w[") || !prop.ends_with("]")) {
Debug::log(LOG, "Invalid selector {}", selector); Debug::log(LOG, "Invalid selector {}", selector);
return false; return false;
@ -378,7 +386,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
bool wantsCountVisible = false; bool wantsCountVisible = false;
int flagCount = 0; int flagCount = 0;
for (auto& flag : prop) { for (auto const& flag : prop) {
if (flag == 't' && wantsOnlyTiled == -1) { if (flag == 't' && wantsOnlyTiled == -1) {
wantsOnlyTiled = 1; wantsOnlyTiled = 1;
flagCount++; flagCount++;
@ -446,7 +454,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
return false; return false;
} }
int count; WORKSPACEID count;
if (wantsCountGroup) if (wantsCountGroup)
count = g_pCompositor->getGroupsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled), count = g_pCompositor->getGroupsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt); wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
@ -506,7 +514,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
void CWorkspace::markInert() { void CWorkspace::markInert() {
m_bInert = true; m_bInert = true;
m_iID = WORKSPACE_INVALID; m_iID = WORKSPACE_INVALID;
m_iMonitorID = -1; m_iMonitorID = MONITOR_INVALID;
m_bVisible = false; m_bVisible = false;
} }

View file

@ -17,16 +17,16 @@ class CWindow;
class CWorkspace { class CWorkspace {
public: public:
static PHLWORKSPACE create(int id, int monitorID, std::string name, bool special = false, bool isEmtpy = true); static PHLWORKSPACE create(WORKSPACEID id, MONITORID monitorID, std::string name, bool special = false, bool isEmpty = true);
// use create() don't use this // use create() don't use this
CWorkspace(int id, int monitorID, std::string name, bool special = false, bool isEmpty = true); CWorkspace(WORKSPACEID id, MONITORID monitorID, std::string name, bool special = false, bool isEmpty = true);
~CWorkspace(); ~CWorkspace();
// Workspaces ID-based have IDs > 0 // Workspaces ID-based have IDs > 0
// and workspaces name-based have IDs starting with -1337 // and workspaces name-based have IDs starting with -1337
int m_iID = -1; WORKSPACEID m_iID = WORKSPACE_INVALID;
std::string m_szName = ""; std::string m_szName = "";
uint64_t m_iMonitorID = -1; MONITORID m_iMonitorID = MONITOR_INVALID;
// Previous workspace ID and name is stored during a workspace change, allowing travel // Previous workspace ID and name is stored during a workspace change, allowing travel
// to the previous workspace. // to the previous workspace.
SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor; SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor;
@ -57,7 +57,7 @@ class CWorkspace {
// last monitor (used on reconnect) // last monitor (used on reconnect)
std::string m_szLastMonitor = ""; std::string m_szLastMonitor = "";
bool m_bWasCreatedEmtpy = true; bool m_bWasCreatedEmpty = true;
bool m_bPersistent = false; bool m_bPersistent = false;
@ -67,7 +67,7 @@ class CWorkspace {
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);
void moveToMonitor(const int&); void moveToMonitor(const MONITORID&);
PHLWINDOW getLastFocusedWindow(); PHLWINDOW getLastFocusedWindow();
void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace); void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace);

View file

@ -119,7 +119,8 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
if (IDX != XKB_MOD_INVALID) if (IDX != XKB_MOD_INVALID)
modifiersState.locked |= (uint32_t)1 << IDX; modifiersState.locked |= (uint32_t)1 << IDX;
updateModifiers(modifiersState.depressed, modifiersState.latched, modifiersState.locked, modifiersState.group); // 0 to avoid mods getting stuck if depressed during reload
updateModifiers(0, 0, modifiersState.locked, modifiersState.group);
} }
for (size_t i = 0; i < LEDNAMES.size(); ++i) { for (size_t i = 0; i < LEDNAMES.size(); ++i) {
@ -177,13 +178,18 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbState) if (xkbState)
xkb_state_unref(xkbState); xkb_state_unref(xkbState);
if (xkbSymState)
xkb_state_unref(xkbSymState);
xkbState = nullptr; xkbState = nullptr;
xkbStaticState = nullptr; xkbStaticState = nullptr;
xkbSymState = nullptr;
if (keymap) { if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this); Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbStaticState = xkb_state_new(keymap); xkbStaticState = xkb_state_new(keymap);
xkbState = xkb_state_new(keymap); xkbState = xkb_state_new(keymap);
xkbSymState = xkb_state_new(keymap);
return; return;
} }
@ -229,6 +235,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(KEYMAP); xkbState = xkb_state_new(KEYMAP);
xkbStaticState = xkb_state_new(KEYMAP); xkbStaticState = xkb_state_new(KEYMAP);
xkbSymState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP); xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT); xkb_context_unref(PCONTEXT);
@ -251,6 +258,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(NEWKEYMAP); xkbState = xkb_state_new(NEWKEYMAP);
xkbStaticState = xkb_state_new(NEWKEYMAP); xkbStaticState = xkb_state_new(NEWKEYMAP);
xkbSymState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP); xkb_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT); xkb_context_unref(PCONTEXT);
@ -331,6 +339,9 @@ void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t l
xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group); xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group);
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, group);
if (!updateModifiersState()) if (!updateModifiersState())
return; return;
@ -381,6 +392,9 @@ void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) {
xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
if (updateModifiersState()) { if (updateModifiersState()) {
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, modifiersState.group);
keyboardEvents.modifiers.emit(SModifiersEvent{ keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed, .depressed = modifiersState.depressed,
.latched = modifiersState.latched, .latched = modifiersState.latched,

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "IHID.hpp" #include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
@ -83,7 +82,8 @@ class IKeyboard : public IHID {
bool keymapOverridden = false; bool keymapOverridden = false;
xkb_layout_index_t activeLayout = 0; xkb_layout_index_t activeLayout = 0;
xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr; xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr,
*xkbSymState = nullptr /* Same as static but gets layouts */;
xkb_keymap* xkbKeymap = nullptr; xkb_keymap* xkbKeymap = nullptr;
struct { struct {

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "IHID.hpp" #include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "IHID.hpp" #include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "IHID.hpp" #include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"

View file

@ -27,7 +27,6 @@ namespace Events {
// Monitor part 2 the sequel // Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame); DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);
DYNLISTENFUNC(monitorStateRequest); DYNLISTENFUNC(monitorStateRequest);
DYNLISTENFUNC(monitorDamage); DYNLISTENFUNC(monitorDamage);
DYNLISTENFUNC(monitorNeedsFrame); DYNLISTENFUNC(monitorNeedsFrame);

View file

@ -85,31 +85,6 @@ void Events::listener_monitorFrame(void* owner, void* data) {
} }
} }
void Events::listener_monitorDestroy(void* owner, void* data) {
CMonitor* pMonitor = (CMonitor*)owner;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->output == pMonitor->output) {
pMonitor = m.get();
break;
}
}
if (!pMonitor)
return;
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name);
pMonitor->onDisconnect(true);
pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false;
Debug::log(LOG, "Removing monitor {} from realMonitors", pMonitor->szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](SP<CMonitor>& el) { return el.get() == pMonitor; });
}
void Events::listener_monitorNeedsFrame(void* owner, void* data) { void Events::listener_monitorNeedsFrame(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner; const auto PMONITOR = (CMonitor*)owner;

View file

@ -12,6 +12,7 @@
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp" #include "../protocols/ToplevelExport.hpp"
#include "../xwayland/XSurface.hpp" #include "../xwayland/XSurface.hpp"
#include "managers/PointerManager.hpp"
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
@ -120,7 +121,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bRequestsFloat = true; PWINDOW->m_bRequestsFloat = true;
} }
PWINDOW->m_bX11ShouldntFocus = PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->m_iX11Type == 2 && !PWINDOW->m_pXWaylandSurface->wantsFocus()); PWINDOW->m_bX11ShouldntFocus = PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_pXWaylandSurface->wantsFocus());
if (PWORKSPACE->m_bDefaultFloating) if (PWORKSPACE->m_bDefaultFloating)
PWINDOW->m_bIsFloating = true; PWINDOW->m_bIsFloating = true;
@ -139,7 +140,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen)) if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen))
requestedClientFSMode = FSMODE_FULLSCREEN; requestedClientFSMode = FSMODE_FULLSCREEN;
for (auto& r : PWINDOW->m_vMatchedRules) { for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) { if (r.szRule.starts_with("monitor")) {
try { try {
const auto MONITORSTR = trim(r.szRule.substr(r.szRule.find(' '))); const auto MONITORSTR = trim(r.szRule.substr(r.szRule.find(' ')));
@ -148,7 +149,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_iMonitorID = PMONITOR->ID; PWINDOW->m_iMonitorID = PMONITOR->ID;
} else { } else {
if (isNumber(MONITORSTR)) { if (isNumber(MONITORSTR)) {
const long int MONITOR = std::stoi(MONITORSTR); const MONITORID MONITOR = std::stoi(MONITORSTR);
if (!g_pCompositor->getMonitorFromID(MONITOR)) if (!g_pCompositor->getMonitorFromID(MONITOR))
PWINDOW->m_iMonitorID = 0; PWINDOW->m_iMonitorID = 0;
else else
@ -322,11 +323,11 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->updateWindowData(); PWINDOW->updateWindowData();
if (PWINDOW->m_bIsFloating) { if (PWINDOW->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(PWINDOW); g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
PWINDOW->m_bCreatedOverFullscreen = true; PWINDOW->m_bCreatedOverFullscreen = true;
// size and move rules // size and move rules
for (auto& r : PWINDOW->m_vMatchedRules) { for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("size")) { 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);
@ -345,6 +346,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Rule size, applying to {}", PWINDOW); Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
PWINDOW->m_vRealSize = Vector2D(SIZEX, SIZEY); PWINDOW->m_vRealSize = Vector2D(SIZEX, SIZEY);
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal();
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal()); g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal());
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
@ -449,7 +451,35 @@ void Events::listener_mapWindow(void* owner, void* data) {
} else { } else {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW); g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
// Set the pseudo size here too so that it doesnt end up being 0x0 bool setPseudo = false;
for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("size")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(PWINDOW);
const auto SIZEX = SIZEXSTR == "max" ?
std::clamp(MAXSIZE.x, 20.0, PMONITOR->vecSize.x) :
(!SIZEXSTR.contains('%') ? std::stoi(SIZEXSTR) : std::stof(SIZEXSTR.substr(0, SIZEXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x);
const auto SIZEY = SIZEYSTR == "max" ?
std::clamp(MAXSIZE.y, 20.0, PMONITOR->vecSize.y) :
(!SIZEYSTR.contains('%') ? std::stoi(SIZEYSTR) : std::stof(SIZEYSTR.substr(0, SIZEYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
setPseudo = true;
PWINDOW->m_vPseudoSize = Vector2D(SIZEX, SIZEY);
PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r.szRule, r.szValue); }
}
}
if (!setPseudo)
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal() - Vector2D(10, 10); PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal() - Vector2D(10, 10);
} }
@ -477,7 +507,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
} }
if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus && if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus &&
(PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && (!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) &&
!g_pInputManager->isConstrained()) { !g_pInputManager->isConstrained()) {
g_pCompositor->focusWindow(PWINDOW); g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA); PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA);
@ -577,7 +607,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->updateWorkspaceWindows(PWINDOW->workspaceID()); g_pCompositor->updateWorkspaceWindows(PWINDOW->workspaceID());
if (PMONITOR && PWINDOW->m_iX11Type == 2) if (PMONITOR && PWINDOW->isX11OverrideRedirect())
PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale; PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale;
} }
@ -648,7 +678,12 @@ void Events::listener_unmapWindow(void* owner, void* data) {
// refocus on a new window if needed // refocus on a new window if needed
if (wasLastWindow) { if (wasLastWindow) {
const auto PWINDOWCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW); static auto FOCUSONCLOSE = CConfigValue<Hyprlang::INT>("input:focus_on_close");
PHLWINDOW PWINDOWCANDIDATE = nullptr;
if (*FOCUSONCLOSE)
PWINDOWCANDIDATE = (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING));
else
PWINDOWCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW);
Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE); Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE);
@ -717,41 +752,35 @@ void Events::listener_commitWindow(void* owner, void* data) {
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize; const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize;
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize; const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize;
if (MAXSIZE > Vector2D{1, 1}) { PWINDOW->clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional<Vector2D>{MAXSIZE} : std::nullopt);
const auto REALSIZE = PWINDOW->m_vRealSize.goal();
Vector2D newSize = REALSIZE;
if (MAXSIZE.x < newSize.x)
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 = REALSIZE - newSize;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.goal() + DELTA / 2.0;
PWINDOW->m_vRealSize = newSize;
g_pXWaylandManager->setWindowSize(PWINDOW, newSize);
g_pHyprRenderer->damageWindow(PWINDOW); g_pHyprRenderer->damageWindow(PWINDOW);
} }
}
if (!PWINDOW->m_pWorkspace->m_bVisible) if (!PWINDOW->m_pWorkspace->m_bVisible)
return; return;
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
PMONITOR->debugLastPresentation(g_pSeatManager->isPointerFrameCommit ? "listener_commitWindow skip" : "listener_commitWindow");
if (g_pSeatManager->isPointerFrameCommit) {
g_pSeatManager->isPointerFrameSkipped = false;
g_pSeatManager->isPointerFrameCommit = false;
} else
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y, g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y,
PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0); PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0);
if (g_pSeatManager->isPointerFrameSkipped) {
g_pPointerManager->sendStoredMovement();
g_pSeatManager->sendPointerFrame();
g_pSeatManager->isPointerFrameCommit = true;
}
if (!PWINDOW->m_bIsX11) { if (!PWINDOW->m_bIsX11) {
PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces(); PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces();
PWINDOW->m_pPopupHead->recheckTree(); PWINDOW->m_pPopupHead->recheckTree();
} }
// tearing: if solitary, redraw it. This still might be a single surface window // tearing: if solitary, redraw it. This still might be a single surface window
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) { if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) {
CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()}; CRegion damageBox{PWINDOW->m_pWLSurface->resource()->accumulateCurrentBufferDamage()};
@ -811,7 +840,7 @@ void Events::listener_activateX11(void* owner, void* data) {
Debug::log(LOG, "X11 Activate request for window {}", PWINDOW); Debug::log(LOG, "X11 Activate request for window {}", PWINDOW);
if (PWINDOW->m_iX11Type == 2) { if (PWINDOW->isX11OverrideRedirect()) {
Debug::log(LOG, "Unmanaged X11 {} requests activate", PWINDOW); Debug::log(LOG, "Unmanaged X11 {} requests activate", PWINDOW);

View file

@ -12,7 +12,7 @@ void CBezierCurve::setup(std::vector<Vector2D>* pVec) {
m_dPoints.emplace_back(Vector2D(0, 0)); m_dPoints.emplace_back(Vector2D(0, 0));
for (auto& p : *pVec) { for (auto const& p : *pVec) {
m_dPoints.push_back(p); m_dPoints.push_back(p);
} }

View file

@ -0,0 +1,54 @@
#pragma once
#include <type_traits>
#define ULL unsigned long long
#define LD long double
constexpr ULL operator""_kB(const ULL BYTES) {
return BYTES * 1024;
}
constexpr ULL operator""_MB(const ULL BYTES) {
return BYTES * 1024 * 1024;
}
constexpr ULL operator""_GB(const ULL BYTES) {
return BYTES * 1024 * 1024 * 1024;
}
constexpr ULL operator""_TB(const ULL BYTES) {
return BYTES * 1024 * 1024 * 1024 * 1024;
}
constexpr LD operator""_kB(const LD BYTES) {
return BYTES * 1024;
}
constexpr LD operator""_MB(const LD BYTES) {
return BYTES * 1024 * 1024;
}
constexpr LD operator""_GB(const LD BYTES) {
return BYTES * 1024 * 1024 * 1024;
}
constexpr LD operator""_TB(const LD BYTES) {
return BYTES * 1024 * 1024 * 1024 * 1024;
}
template <typename T>
using __acceptable_byte_operation_type = typename std::enable_if<std::is_trivially_constructible<T, ULL>::value || std::is_trivially_constructible<T, LD>::value>::type;
template <typename X, typename = __acceptable_byte_operation_type<X>>
constexpr X kBtoBytes(const X kB) {
return kB * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
constexpr X MBtoBytes(const X MB) {
return MB * 1024 * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
constexpr X GBtoBytes(const X GB) {
return GB * 1024 * 1024 * 1024;
}
template <typename X, typename = __acceptable_byte_operation_type<X>>
constexpr X TBtoBytes(const X TB) {
return TB * 1024 * 1024 * 1024 * 1024;
}
#undef ULL
#undef LD

View file

@ -21,6 +21,6 @@ CColor::CColor(uint64_t hex) {
this->a = ALPHA(hex); this->a = ALPHA(hex);
} }
uint32_t CColor::getAsHex() { uint32_t CColor::getAsHex() const {
return (uint32_t)(a * 255.f) * 0x1000000 + (uint32_t)(r * 255.f) * 0x10000 + (uint32_t)(g * 255.f) * 0x100 + (uint32_t)(b * 255.f) * 0x1; return (uint32_t)(a * 255.f) * 0x1000000 + (uint32_t)(r * 255.f) * 0x10000 + (uint32_t)(g * 255.f) * 0x100 + (uint32_t)(b * 255.f) * 0x1;
} }

View file

@ -10,7 +10,8 @@ class CColor {
float r = 0, g = 0, b = 0, a = 1.f; float r = 0, g = 0, b = 0, a = 1.f;
uint32_t getAsHex(); // AR32
uint32_t getAsHex() const;
CColor operator-(const CColor& c2) const { CColor operator-(const CColor& c2) const {
return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a); return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);

View file

@ -252,7 +252,7 @@ DRMFormat FormatUtils::shmToDRM(SHMFormat shm) {
} }
const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) { const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
for (auto& fmt : GLES3_FORMATS) { for (auto const& fmt : GLES3_FORMATS) {
if (fmt.drmFormat == drm) if (fmt.drmFormat == drm)
return &fmt; return &fmt;
} }
@ -261,7 +261,7 @@ const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
} }
const SPixelFormat* FormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) { const SPixelFormat* FormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) {
for (auto& fmt : GLES3_FORMATS) { for (auto const& fmt : GLES3_FORMATS) {
if (fmt.glFormat == (int)glFormat && fmt.glType == (int)glType && fmt.withAlpha == alpha) if (fmt.glFormat == (int)glFormat && fmt.glType == (int)glType && fmt.withAlpha == alpha)
return &fmt; return &fmt;
} }

View file

@ -180,7 +180,7 @@ void handleNoop(struct wl_listener* listener, void* data) {
std::string escapeJSONStrings(const std::string& str) { std::string escapeJSONStrings(const std::string& str) {
std::ostringstream oss; std::ostringstream oss;
for (auto& c : str) { for (auto const& c : str) {
switch (c) { switch (c) {
case '"': oss << "\\\""; break; case '"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break; case '\\': oss << "\\\\"; break;
@ -249,17 +249,17 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
return {WORKSPACE_INVALID}; return {WORKSPACE_INVALID};
} }
std::set<int> invalidWSes; std::set<WORKSPACEID> invalidWSes;
if (same_mon) { if (same_mon) {
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) { for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) {
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor); const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID)) if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID))
invalidWSes.insert(rule.workspaceId); invalidWSes.insert(rule.workspaceId);
} }
} }
int id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0; WORKSPACEID id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0;
while (++id < INT_MAX) { while (++id < LONG_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id); const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0)) { if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0)) {
result.id = id; result.id = id;
@ -296,18 +296,18 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
result.id = (int)PLUSMINUSRESULT.value(); result.id = (int)PLUSMINUSRESULT.value();
int remains = (int)result.id; WORKSPACEID remains = result.id;
std::set<int> invalidWSes; std::set<WORKSPACEID> invalidWSes;
// Collect all the workspaces we can't jump to. // Collect all the workspaces we can't jump to.
for (auto& ws : g_pCompositor->m_vWorkspaces) { for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID)) { if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID)) {
// Can't jump to this workspace // Can't jump to this workspace
invalidWSes.insert(ws->m_iID); invalidWSes.insert(ws->m_iID);
} }
} }
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) { for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) {
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor); const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) { if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) {
// Can't be invalid // Can't be invalid
@ -318,8 +318,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
} }
// Prepare all named workspaces in case when we need them // Prepare all named workspaces in case when we need them
std::vector<int> namedWSes; std::vector<WORKSPACEID> namedWSes;
for (auto& ws : g_pCompositor->m_vWorkspaces) { for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0) if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0)
continue; continue;
@ -347,19 +347,19 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
} else { } else {
// Just take a blind guess at where we'll probably end up // Just take a blind guess at where we'll probably end up
int activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1; WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1;
int predictedWSID = activeWSID + remains; WORKSPACEID predictedWSID = activeWSID + remains;
int remainingWSes = 0; int remainingWSes = 0;
char walkDir = in[1]; char walkDir = in[1];
// sanitize. 0 means invalid oob in - // sanitize. 0 means invalid oob in -
predictedWSID = std::max(predictedWSID, 0); predictedWSID = std::max(predictedWSID, static_cast<int64_t>(0));
// Count how many invalidWSes are in between (how bad the prediction was) // Count how many invalidWSes are in between (how bad the prediction was)
int beginID = in[1] == '+' ? activeWSID + 1 : predictedWSID; WORKSPACEID beginID = in[1] == '+' ? activeWSID + 1 : predictedWSID;
int endID = in[1] == '+' ? predictedWSID : activeWSID; WORKSPACEID endID = in[1] == '+' ? predictedWSID : activeWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >= auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) { for (auto it = begin; it != invalidWSes.end() && *it <= endID; it++) {
remainingWSes++; remainingWSes++;
} }
@ -367,7 +367,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
if (activeWSID < 0) { if (activeWSID < 0) {
// Behaviour similar to 'm' // Behaviour similar to 'm'
// Find current // Find current
int currentItem = -1; size_t currentItem = -1;
for (size_t i = 0; i < namedWSes.size(); i++) { for (size_t i = 0; i < namedWSes.size(); i++) {
if (namedWSes[i] == activeWSID) { if (namedWSes[i] == activeWSID) {
currentItem = i; currentItem = i;
@ -376,15 +376,15 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
} }
currentItem += remains; currentItem += remains;
currentItem = std::max(currentItem, 0); currentItem = std::max(currentItem, static_cast<size_t>(0));
if (currentItem >= (int)namedWSes.size()) { if (currentItem >= namedWSes.size()) {
// At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0 // At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0
int diff = currentItem - (namedWSes.size() - 1); size_t diff = currentItem - (namedWSes.size() - 1);
predictedWSID = diff; predictedWSID = diff;
int beginID = 1; WORKSPACEID beginID = 1;
int endID = predictedWSID; WORKSPACEID endID = predictedWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >= auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) { for (auto it = begin; it != invalidWSes.end() && *it <= endID; it++) {
remainingWSes++; remainingWSes++;
} }
walkDir = '+'; walkDir = '+';
@ -397,10 +397,10 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// Go in the search direction for remainingWSes // Go in the search direction for remainingWSes
// The performance impact is directly proportional to the number of open and bound workspaces // The performance impact is directly proportional to the number of open and bound workspaces
int finalWSID = predictedWSID; WORKSPACEID finalWSID = predictedWSID;
if (walkDir == '-') { if (walkDir == '-') {
int beginID = finalWSID; WORKSPACEID beginID = finalWSID;
int curID = finalWSID; WORKSPACEID curID = finalWSID;
while (--curID > 0 && remainingWSes > 0) { while (--curID > 0 && remainingWSes > 0) {
if (!invalidWSes.contains(curID)) { if (!invalidWSes.contains(curID)) {
remainingWSes--; remainingWSes--;
@ -411,9 +411,9 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
if (namedWSes.size()) { if (namedWSes.size()) {
// Go to the named workspaces // Go to the named workspaces
// Need remainingWSes more // Need remainingWSes more
int namedWSIdx = namedWSes.size() - remainingWSes; auto namedWSIdx = namedWSes.size() - remainingWSes;
// Sanitze // Sanitze
namedWSIdx = std::clamp(namedWSIdx, 0, (int)namedWSes.size() - 1); namedWSIdx = std::clamp(namedWSIdx, static_cast<size_t>(0), namedWSes.size() - static_cast<size_t>(1));
finalWSID = namedWSes[namedWSIdx]; finalWSID = namedWSes[namedWSIdx];
} else { } else {
// Couldn't find valid workspace in negative direction, search last first one back up positive direction // Couldn't find valid workspace in negative direction, search last first one back up positive direction
@ -425,7 +425,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
} }
} }
if (walkDir == '+') { if (walkDir == '+') {
int curID = finalWSID; WORKSPACEID curID = finalWSID;
while (++curID < INT32_MAX && remainingWSes > 0) { while (++curID < INT32_MAX && remainingWSes > 0) {
if (!invalidWSes.contains(curID)) { if (!invalidWSes.contains(curID)) {
remainingWSes--; remainingWSes--;
@ -462,8 +462,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// result now has +/- what we should move on mon // result now has +/- what we should move on mon
int remains = (int)result.id; int remains = (int)result.id;
std::vector<int> validWSes; std::vector<WORKSPACEID> validWSes;
for (auto& ws : g_pCompositor->m_vWorkspaces) { for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID && !onAllMonitors)) if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID && !onAllMonitors))
continue; continue;
@ -472,7 +472,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
std::sort(validWSes.begin(), validWSes.end()); std::sort(validWSes.begin(), validWSes.end());
int currentItem = -1; ssize_t currentItem = -1;
if (absolute) { if (absolute) {
// 1-index // 1-index
@ -481,7 +481,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// clamp // clamp
if (currentItem < 0) { if (currentItem < 0) {
currentItem = 0; currentItem = 0;
} else if (currentItem >= (int)validWSes.size()) { } else if (currentItem >= (ssize_t)validWSes.size()) {
currentItem = validWSes.size() - 1; currentItem = validWSes.size() - 1;
} }
} else { } else {
@ -489,8 +489,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
remains = remains < 0 ? -((-remains) % validWSes.size()) : remains % validWSes.size(); remains = remains < 0 ? -((-remains) % validWSes.size()) : remains % validWSes.size();
// get the current item // get the current item
int activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1; WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1;
for (size_t i = 0; i < validWSes.size(); i++) { for (ssize_t i = 0; i < (ssize_t)validWSes.size(); i++) {
if (validWSes[i] == activeWSID) { if (validWSes[i] == activeWSID) {
currentItem = i; currentItem = i;
break; break;
@ -501,7 +501,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
currentItem += remains; currentItem += remains;
// sanitize // sanitize
if (currentItem >= (int)validWSes.size()) { if (currentItem >= (ssize_t)validWSes.size()) {
currentItem = currentItem % validWSes.size(); currentItem = currentItem % validWSes.size();
} else if (currentItem < 0) { } else if (currentItem < 0) {
currentItem = validWSes.size() + currentItem; currentItem = validWSes.size() + currentItem;
@ -547,7 +547,7 @@ std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceNa
const std::string workspaceRule = "workspace " + inWorkspaceName; const std::string workspaceRule = "workspace " + inWorkspaceName;
if (cmd[0] == '[') { if (cmd[0] == '[') {
const int closingBracketIdx = cmd.find_last_of(']'); const auto closingBracketIdx = cmd.find_last_of(']');
auto tmpRules = cmd.substr(1, closingBracketIdx - 1); auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
cmd = cmd.substr(closingBracketIdx + 1); cmd = cmd.substr(closingBracketIdx + 1);
@ -628,27 +628,6 @@ void logSystemInfo() {
Debug::log(NONE, "{}", execAndGet("cat /etc/os-release")); Debug::log(NONE, "{}", execAndGet("cat /etc/os-release"));
} }
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr) {
memset(mat, 0, sizeof(*mat) * 9);
const float* t = transforms[tr];
float x = 2.0f / w;
float y = 2.0f / h;
// Rotation + reflection
mat[0] = x * t[0];
mat[1] = x * t[1];
mat[3] = y * t[3];
mat[4] = y * t[4];
// Translation
mat[2] = -copysign(1.0f, mat[0] + mat[1]);
mat[5] = -copysign(1.0f, mat[3] + mat[4]);
// Identity
mat[8] = 1.0f;
}
int64_t getPPIDof(int64_t pid) { int64_t getPPIDof(int64_t pid) {
#if defined(KERN_PROC_PID) #if defined(KERN_PROC_PID)
int mib[] = { int mib[] = {
@ -785,13 +764,13 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
#ifdef HAS_EXECINFO #ifdef HAS_EXECINFO
void* bt[1024]; void* bt[1024];
size_t btSize; int btSize;
char** btSymbols; char** btSymbols;
btSize = backtrace(bt, 1024); btSize = backtrace(bt, 1024);
btSymbols = backtrace_symbols(bt, btSize); btSymbols = backtrace_symbols(bt, btSize);
for (size_t i = 0; i < btSize; ++i) { for (auto 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 #else

View file

@ -6,6 +6,8 @@
#include "math/Math.hpp" #include "math/Math.hpp"
#include <vector> #include <vector>
#include <format> #include <format>
#include "../SharedDefs.hpp"
#include "../macros.hpp"
struct SCallstackFrameInfo { struct SCallstackFrameInfo {
void* adr = nullptr; void* adr = nullptr;
@ -13,7 +15,7 @@ struct SCallstackFrameInfo {
}; };
struct SWorkspaceIDName { struct SWorkspaceIDName {
int id = -1; WORKSPACEID id = WORKSPACE_INVALID;
std::string name; std::string name;
}; };
@ -32,7 +34,6 @@ int64_t getPPIDof(int64_t pid);
int64_t configStringToInt(const std::string&); int64_t configStringToInt(const std::string&);
Vector2D configStringToVector2D(const std::string&); Vector2D configStringToVector2D(const std::string&);
std::optional<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);
double normalizeAngleRad(double ang); double normalizeAngleRad(double ang);
std::vector<SCallstackFrameInfo> getBacktrace(); std::vector<SCallstackFrameInfo> getBacktrace();
void throwError(const std::string& err); void throwError(const std::string& err);

View file

@ -1,6 +1,7 @@
#include "Monitor.hpp" #include "Monitor.hpp"
#include "MiscFunctions.hpp" #include "MiscFunctions.hpp"
#include "math/Math.hpp" #include "math/Math.hpp"
#include "sync/SyncReleaser.hpp"
#include "../Compositor.hpp" #include "../Compositor.hpp"
#include "../config/ConfigValue.hpp" #include "../config/ConfigValue.hpp"
#include "../protocols/GammaControl.hpp" #include "../protocols/GammaControl.hpp"
@ -8,13 +9,18 @@
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp" #include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp" #include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Output.hpp" #include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp" #include "../managers/PointerManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp" #include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp> #include <aquamarine/output/Output.hpp>
#include "debug/Log.hpp"
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
int ratHandler(void* data) { int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data); g_pHyprRenderer->renderMonitor((CMonitor*)data);
@ -22,7 +28,7 @@ int ratHandler(void* data) {
return 1; return 1;
} }
CMonitor::CMonitor() : state(this) { CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : state(this), output(output_) {
; ;
} }
@ -31,6 +37,7 @@ CMonitor::~CMonitor() {
} }
void CMonitor::onConnect(bool noRule) { void CMonitor::onConnect(bool noRule) {
CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }};
if (output->supportsExplicit) { if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD()); inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
@ -38,13 +45,26 @@ void CMonitor::onConnect(bool noRule) {
} }
listeners.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); }); listeners.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); });
listeners.destroy = output->events.destroy.registerListener([this](std::any d) { Events::listener_monitorDestroy(this, nullptr); });
listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); }); listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); });
listeners.needsFrame = listeners.needsFrame =
output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); }); output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });
listeners.presented = output->events.present.registerListener([this](std::any d) { listeners.presented = output->events.present.registerListener([this](std::any d) {
auto E = std::any_cast<Aquamarine::IOutput::SPresentEvent>(d); auto E = std::any_cast<Aquamarine::IOutput::SPresentEvent>(d);
PROTO::presentation->onPresented(this, E.when, E.refresh, E.seq, E.flags); PROTO::presentation->onPresented(self.lock(), E.when, E.refresh, E.seq, E.flags);
});
listeners.destroy = output->events.destroy.registerListener([this](std::any d) {
Debug::log(LOG, "Destroy called for monitor {}", szName);
onDisconnect(true);
output = nullptr;
m_bRenderingInitPassed = false;
Debug::log(LOG, "Removing monitor {} from realMonitors", szName);
std::erase_if(g_pCompositor->m_vRealMonitors, [&](SP<CMonitor>& el) { return el.get() == this; });
}); });
listeners.state = output->events.state.registerListener([this](std::any d) { listeners.state = output->events.state.registerListener([this](std::any d) {
@ -77,6 +97,7 @@ void CMonitor::onConnect(bool noRule) {
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM; tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
if (m_bEnabled) { if (m_bEnabled) {
output->state->resetExplicitFences();
output->state->setEnabled(true); output->state->setEnabled(true);
state.commit(); state.commit();
return; return;
@ -96,11 +117,12 @@ void CMonitor::onConnect(bool noRule) {
createdByUser = true; // should be true. WL and Headless backends should be addable / removable createdByUser = true; // should be true. WL and Headless backends should be addable / removable
// get monitor rule that matches // get monitor rule that matches
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this); SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(self.lock());
// if it's disabled, disable and ignore // if it's disabled, disable and ignore
if (monitorRule.disabled) { if (monitorRule.disabled) {
output->state->resetExplicitFences();
output->state->setEnabled(false); output->state->setEnabled(false);
if (!state.commit()) if (!state.commit())
@ -137,6 +159,7 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true; m_bEnabled = true;
output->state->resetExplicitFences();
output->state->setEnabled(true); output->state->setEnabled(true);
// set mode, also applies // set mode, also applies
@ -152,7 +175,7 @@ void CMonitor::onConnect(bool noRule) {
setupDefaultWS(monitorRule); setupDefaultWS(monitorRule);
for (auto& ws : g_pCompositor->m_vWorkspaces) { for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (!valid(ws)) if (!valid(ws))
continue; continue;
@ -173,10 +196,6 @@ void CMonitor::onConnect(bool noRule) {
if (!activeMonitorRule.mirrorOf.empty()) if (!activeMonitorRule.mirrorOf.empty())
setMirror(activeMonitorRule.mirrorOf); setMirror(activeMonitorRule.mirrorOf);
g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName});
g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)});
EMIT_HOOK_EVENT("monitorAdded", this);
if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet
g_pCompositor->setActiveMonitor(this); g_pCompositor->setActiveMonitor(this);
@ -188,7 +207,7 @@ void CMonitor::onConnect(bool noRule) {
// verify last mon valid // verify last mon valid
bool found = false; bool found = false;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
if (m == g_pCompositor->m_pLastMonitor) { if (m == g_pCompositor->m_pLastMonitor) {
found = true; found = true;
break; break;
@ -205,9 +224,20 @@ void CMonitor::onConnect(bool noRule) {
PROTO::gamma->applyGammaToState(this); PROTO::gamma->applyGammaToState(this);
events.connect.emit(); events.connect.emit();
g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName});
g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)});
EMIT_HOOK_EVENT("monitorAdded", this);
} }
void CMonitor::onDisconnect(bool destroy) { void CMonitor::onDisconnect(bool destroy) {
CScopeGuard x = {[this]() {
if (g_pCompositor->m_bIsShuttingDown)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
g_pCompositor->arrangeMonitors();
}};
if (renderTimer) { if (renderTimer) {
wl_event_source_remove(renderTimer); wl_event_source_remove(renderTimer);
@ -223,7 +253,7 @@ void CMonitor::onDisconnect(bool destroy) {
// Cleanup everything. Move windows back, snap cursor, shit. // Cleanup everything. Move windows back, snap cursor, shit.
CMonitor* BACKUPMON = nullptr; CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) { if (m.get() != this) {
BACKUPMON = m.get(); BACKUPMON = m.get();
break; break;
@ -240,7 +270,7 @@ void CMonitor::onDisconnect(bool destroy) {
} }
if (!mirrors.empty()) { if (!mirrors.empty()) {
for (auto& m : mirrors) { for (auto const& m : mirrors) {
m->setMirror(""); m->setMirror("");
} }
@ -253,7 +283,7 @@ void CMonitor::onDisconnect(bool destroy) {
listeners.commit.reset(); listeners.commit.reset();
for (size_t i = 0; i < 4; ++i) { for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) { for (auto const& ls : m_aLayerSurfaceLayers[i]) {
if (ls->layerSurface && !ls->fadingOut) if (ls->layerSurface && !ls->fadingOut)
ls->layerSurface->sendClosed(); ls->layerSurface->sendClosed();
} }
@ -262,9 +292,6 @@ void CMonitor::onDisconnect(bool destroy) {
Debug::log(LOG, "Removed monitor {}!", szName); Debug::log(LOG, "Removed monitor {}!", szName);
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
if (!BACKUPMON) { if (!BACKUPMON) {
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend."); Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
g_pCompositor->enterUnsafeState(); g_pCompositor->enterUnsafeState();
@ -279,13 +306,13 @@ void CMonitor::onDisconnect(bool destroy) {
// move workspaces // move workspaces
std::deque<PHLWORKSPACE> wspToMove; std::deque<PHLWORKSPACE> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) { if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) {
wspToMove.push_back(w); wspToMove.push_back(w);
} }
} }
for (auto& w : wspToMove) { for (auto const& w : wspToMove) {
w->m_szLastMonitor = szName; w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true); w->startAnim(true, true, true);
@ -300,6 +327,7 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace->m_bVisible = false; activeWorkspace->m_bVisible = false;
activeWorkspace.reset(); activeWorkspace.reset();
output->state->resetExplicitFences();
output->state->setEnabled(false); output->state->setEnabled(false);
if (!state.commit()) if (!state.commit())
@ -312,7 +340,7 @@ void CMonitor::onDisconnect(bool destroy) {
int mostHz = 0; int mostHz = 0;
CMonitor* pMonitorMostHz = nullptr; CMonitor* pMonitorMostHz = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
if (m->refreshRate > mostHz && m.get() != this) { if (m->refreshRate > mostHz && m.get() != this) {
pMonitorMostHz = m.get(); pMonitorMostHz = m.get();
mostHz = m->refreshRate; mostHz = m->refreshRate;
@ -357,7 +385,7 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN; *PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
// keep requested minimum refresh rate // keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) { if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) {
// damage whole screen because some previous cursor box damages were skipped // damage whole screen because some previous cursor box damages were skipped
damage.damageEntire(); damage.damageEntire();
return false; return false;
@ -382,8 +410,8 @@ bool CMonitor::matchesStaticSelector(const std::string& selector) const {
} }
} }
int CMonitor::findAvailableDefaultWS() { WORKSPACEID CMonitor::findAvailableDefaultWS() {
for (size_t i = 1; i < INT32_MAX; ++i) { for (WORKSPACEID i = 1; i < LONG_MAX; ++i) {
if (g_pCompositor->getWorkspaceByID(i)) if (g_pCompositor->getWorkspaceByID(i))
continue; continue;
@ -393,7 +421,7 @@ int CMonitor::findAvailableDefaultWS() {
return i; return i;
} }
return INT32_MAX; // shouldn't be reachable return LONG_MAX; // shouldn't be reachable
} }
void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) { void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
@ -468,7 +496,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
pMirrorOf = nullptr; pMirrorOf = nullptr;
// set rule // set rule
const auto RULE = g_pConfigManager->getMonitorRuleFor(*this); const auto RULE = g_pConfigManager->getMonitorRuleFor(self.lock());
vecPosition = RULE.offset; vecPosition = RULE.offset;
@ -496,7 +524,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
g_pHyprRenderer->applyMonitorRule(this, (SMonitorRule*)&RULE, true); // will apply the offset and stuff g_pHyprRenderer->applyMonitorRule(this, (SMonitorRule*)&RULE, true); // will apply the offset and stuff
} else { } else {
CMonitor* BACKUPMON = nullptr; CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) { for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) { if (m.get() != this) {
BACKUPMON = m.get(); BACKUPMON = m.get();
break; break;
@ -505,13 +533,13 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
// move all the WS // move all the WS
std::deque<PHLWORKSPACE> wspToMove; std::deque<PHLWORKSPACE> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) { for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) { if (w->m_iMonitorID == ID) {
wspToMove.push_back(w); wspToMove.push_back(w);
} }
} }
for (auto& w : wspToMove) { for (auto const& w : wspToMove) {
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true); w->startAnim(true, true, true);
} }
@ -585,7 +613,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
pWorkspace->startAnim(true, ANIMTOLEFT); pWorkspace->startAnim(true, ANIMTOLEFT);
// move pinned windows // move pinned windows
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == POLDWORKSPACE && w->m_bPinned) if (w->m_pWorkspace == POLDWORKSPACE && w->m_bPinned)
w->moveToWorkspace(pWorkspace); w->moveToWorkspace(pWorkspace);
} }
@ -631,7 +659,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
g_pCompositor->updateFullscreenFadeOnWorkspace(activeSpecialWorkspace); g_pCompositor->updateFullscreenFadeOnWorkspace(activeSpecialWorkspace);
} }
void CMonitor::changeWorkspace(const int& id, bool internal, bool noMouseMove, bool noFocus) { void CMonitor::changeWorkspace(const WORKSPACEID& id, bool internal, bool noMouseMove, bool noFocus) {
changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal, noMouseMove, noFocus); changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal, noMouseMove, noFocus);
} }
@ -694,14 +722,14 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (animate) if (animate)
pWorkspace->startAnim(true, true); pWorkspace->startAnim(true, true);
for (auto& w : g_pCompositor->m_vWindows) { for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == pWorkspace) { if (w->m_pWorkspace == pWorkspace) {
w->m_iMonitorID = ID; w->m_iMonitorID = ID;
w->updateSurfaceScaleTransformDetails(); w->updateSurfaceScaleTransformDetails();
w->setAnimationsToMove(); w->setAnimationsToMove();
const auto MIDDLE = w->middle(); 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 (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && !w->isX11OverrideRedirect()) {
// if it's floating and the middle isnt on the current mon, move it to the center // if it's floating and the middle isnt on the current mon, move it to the center
const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE); const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE);
Vector2D pos = w->m_vRealPosition.goal(); Vector2D pos = w->m_vRealPosition.goal();
@ -738,7 +766,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pCompositor->updateSuspendedStates(); g_pCompositor->updateSuspendedStates();
} }
void CMonitor::setSpecialWorkspace(const int& id) { void CMonitor::setSpecialWorkspace(const WORKSPACEID& id) {
setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id)); setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id));
} }
@ -751,19 +779,16 @@ Vector2D CMonitor::middle() {
} }
void CMonitor::updateMatrix() { void CMonitor::updateMatrix() {
matrixIdentity(projMatrix.data()); projMatrix = Mat3x3::identity();
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) { if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
matrixTranslate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0); projMatrix.translate(vecPixelSize / 2.0).transform(wlTransformToHyprutils(transform)).translate(-vecTransformedSize / 2.0);
matrixTransform(projMatrix.data(), wlTransformToHyprutils(transform));
matrixTranslate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
} }
int64_t CMonitor::activeWorkspaceID() { WORKSPACEID CMonitor::activeWorkspaceID() {
return activeWorkspace ? activeWorkspace->m_iID : 0; return activeWorkspace ? activeWorkspace->m_iID : 0;
} }
int64_t CMonitor::activeSpecialWorkspaceID() { WORKSPACEID CMonitor::activeSpecialWorkspaceID() {
return activeSpecialWorkspace ? activeSpecialWorkspace->m_iID : 0; return activeSpecialWorkspace ? activeSpecialWorkspace->m_iID : 0;
} }
@ -771,20 +796,28 @@ CBox CMonitor::logicalBox() {
return {vecPosition, vecSize}; return {vecPosition, vecSize};
} }
static void onDoneSource(void* data) { void CMonitor::scheduleDone() {
auto pMonitor = (CMonitor*)data; if (doneScheduled)
if (!PROTO::outputs.contains(pMonitor->szName))
return; return;
PROTO::outputs.at(pMonitor->szName)->sendDone(); doneScheduled = true;
g_pEventLoopManager->doLater([M = self] {
if (!M) // if M is gone, we got destroyed, doesn't matter.
return;
if (!PROTO::outputs.contains(M->szName))
return;
PROTO::outputs.at(M->szName)->sendDone();
M->doneScheduled = false;
});
} }
void CMonitor::scheduleDone() { void CMonitor::setCTM(const Mat3x3& ctm_) {
if (doneSource) ctm = ctm_;
return; ctmUpdated = true;
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME);
doneSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::onDoneSource, this);
} }
bool CMonitor::attemptDirectScanout() { bool CMonitor::attemptDirectScanout() {
@ -808,28 +841,92 @@ bool CMonitor::attemptDirectScanout() {
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */) if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
return false; return false;
Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt", (uintptr_t)PSURFACE.get());
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats // FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!! // and comes from the appropriate device. This may implode on multi-gpu!!
const auto params = PSURFACE->current.buffer->buffer->dmabuf();
// scanout buffer isn't dmabuf, so no scanout
if (!params.success)
return false;
// entering into scanout, so save monitor format
if (lastScanout.expired())
prevDrmFormat = drmFormat;
if (drmFormat != params.format) {
output->state->setFormat(params.format);
drmFormat = params.format;
}
output->state->setBuffer(PSURFACE->current.buffer->buffer.lock()); output->state->setBuffer(PSURFACE->current.buffer->buffer.lock());
output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
if (!state.test()) if (!state.test()) {
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
return false; return false;
}
auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings();
// wait for the explicit fence if present, and if kms explicit is allowed
bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.acquireTimeline && PSURFACE->syncobj->current.acquireTimeline->timeline && explicitOptions.explicitKMSEnabled;
int explicitWaitFD = -1;
if (DOEXPLICIT) {
explicitWaitFD = PSURFACE->syncobj->current.acquireTimeline->timeline->exportAsSyncFileFD(PSURFACE->syncobj->current.acquirePoint);
if (explicitWaitFD < 0)
Debug::log(TRACE, "attemptDirectScanout: failed to acquire an explicit wait fd");
}
DOEXPLICIT = DOEXPLICIT && explicitWaitFD >= 0;
auto cleanup = CScopeGuard([explicitWaitFD, this]() {
output->state->resetExplicitFences();
if (explicitWaitFD >= 0)
close(explicitWaitFD);
});
timespec now; timespec now;
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS"); PSURFACE->presentFeedback(&now, self.lock());
PSURFACE->presentFeedback(&now, this, true);
output->state->addDamage(CBox{{}, vecPixelSize}); output->state->addDamage(CBox{{}, vecPixelSize});
output->state->resetExplicitFences();
if (state.commit()) { if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", explicitWaitFD);
output->state->setExplicitInFence(explicitWaitFD);
}
bool ok = output->commit();
if (!ok && DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches.");
output->state->resetExplicitFences();
ok = output->commit();
}
if (ok) {
if (lastScanout.expired()) { if (lastScanout.expired()) {
lastScanout = PCANDIDATE; lastScanout = PCANDIDATE;
Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle); Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle);
} }
// delay explicit sync feedback until kms release of the buffer
if (DOEXPLICIT) {
Debug::log(TRACE, "attemptDirectScanout: Delaying explicit sync release feedback until kms release");
PSURFACE->current.buffer->releaser->drop();
PSURFACE->current.buffer->buffer->hlEvents.backendRelease2 = PSURFACE->current.buffer->buffer->events.backendRelease.registerListener([PSURFACE](std::any d) {
const bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->syncobj->current.releaseTimeline && PSURFACE->syncobj->current.releaseTimeline->timeline;
if (DOEXPLICIT)
PSURFACE->syncobj->current.releaseTimeline->timeline->signal(PSURFACE->syncobj->current.releasePoint);
});
}
} else { } else {
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
lastScanout.reset(); lastScanout.reset();
return false; return false;
} }
@ -837,6 +934,11 @@ bool CMonitor::attemptDirectScanout() {
return true; return true;
} }
void CMonitor::debugLastPresentation(const std::string& message) {
Debug::log(TRACE, "{} (last presentation {} - {} fps)", message, lastPresentationTimer.getMillis(),
lastPresentationTimer.getMillis() > 0 ? 1000.0f / lastPresentationTimer.getMillis() : 0.0f);
}
CMonitorState::CMonitorState(CMonitor* owner) { CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner; m_pOwner = owner;
} }
@ -846,17 +948,20 @@ CMonitorState::~CMonitorState() {
} }
void CMonitorState::ensureBufferPresent() { void CMonitorState::ensureBufferPresent() {
if (!m_pOwner->output->state->state().enabled) { const auto STATE = m_pOwner->output->state->state();
if (!STATE.enabled) {
Debug::log(TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled"); Debug::log(TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled");
return; return;
} }
if (m_pOwner->output->state->state().buffer) if (STATE.buffer) {
if (const auto params = STATE.buffer->dmabuf(); params.success && params.format == m_pOwner->drmFormat)
return; return;
}
// this is required for modesetting being possible and might be missing in case of first tests in the renderer // this is required for modesetting being possible and might be missing in case of first tests in the renderer
// where we test modes and buffers // where we test modes and buffers
Debug::log(LOG, "CMonitorState::ensureBufferPresent: no buffer, attaching one from the swapchain for modeset being possible"); Debug::log(LOG, "CMonitorState::ensureBufferPresent: no buffer or mismatched format, attaching one from the swapchain for modeset being possible");
m_pOwner->output->state->setBuffer(m_pOwner->output->swapchain->next(nullptr)); m_pOwner->output->state->setBuffer(m_pOwner->output->swapchain->next(nullptr));
m_pOwner->output->swapchain->rollback(); // restore the counter, don't advance the swapchain m_pOwner->output->swapchain->rollback(); // restore the counter, don't advance the swapchain
} }
@ -890,7 +995,7 @@ bool CMonitorState::updateSwapchain() {
Debug::log(WARN, "updateSwapchain: No mode?"); Debug::log(WARN, "updateSwapchain: No mode?");
return true; return true;
} }
options.format = STATE.drmFormat; options.format = m_pOwner->drmFormat;
options.scanout = true; options.scanout = true;
options.length = 2; options.length = 2;
options.size = MODE->pixelSize; options.size = MODE->pixelSize;

View file

@ -59,7 +59,7 @@ class CMonitorState {
class CMonitor { class CMonitor {
public: public:
CMonitor(); CMonitor(SP<Aquamarine::IOutput> output);
~CMonitor(); ~CMonitor();
Vector2D vecPosition = Vector2D(-1, -1); // means unset Vector2D vecPosition = Vector2D(-1, -1); // means unset
@ -70,7 +70,7 @@ class CMonitor {
bool primary = false; bool primary = false;
uint64_t ID = -1; MONITORID ID = MONITOR_INVALID;
PHLWORKSPACE activeWorkspace = nullptr; PHLWORKSPACE activeWorkspace = nullptr;
PHLWORKSPACE activeSpecialWorkspace = nullptr; PHLWORKSPACE activeSpecialWorkspace = nullptr;
float setScale = 1; // scale set by cfg float setScale = 1; // scale set by cfg
@ -96,10 +96,12 @@ class CMonitor {
bool scheduledRecalc = false; bool scheduledRecalc = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
float xwaylandScale = 1.f; float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0}; Mat3x3 projMatrix;
std::optional<Vector2D> forceSize; std::optional<Vector2D> forceSize;
SP<Aquamarine::SOutputMode> currentMode; SP<Aquamarine::SOutputMode> currentMode;
SP<Aquamarine::CSwapchain> cursorSwapchain; SP<Aquamarine::CSwapchain> cursorSwapchain;
uint32_t drmFormat = DRM_FORMAT_INVALID;
uint32_t prevDrmFormat = DRM_FORMAT_INVALID;
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.
@ -121,7 +123,6 @@ class CMonitor {
// explicit sync // explicit sync
SP<CSyncTimeline> inTimeline; SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline; SP<CSyncTimeline> outTimeline;
uint64_t lastWaitPoint = 0;
uint64_t commitSeq = 0; uint64_t commitSeq = 0;
WP<CMonitor> self; WP<CMonitor> self;
@ -130,6 +131,10 @@ class CMonitor {
CMonitor* pMirrorOf = nullptr; CMonitor* pMirrorOf = nullptr;
std::vector<CMonitor*> mirrors; std::vector<CMonitor*> mirrors;
// ctm
Mat3x3 ctm = Mat3x3::identity();
bool ctmUpdated = false;
// for tearing // for tearing
PHLWINDOWREF solitaryClient; PHLWINDOWREF solitaryClient;
@ -167,17 +172,20 @@ class CMonitor {
bool matchesStaticSelector(const std::string& selector) const; bool matchesStaticSelector(const std::string& selector) const;
float getDefaultScale(); float getDefaultScale();
void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void changeWorkspace(const int& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false);
void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace); void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace);
void setSpecialWorkspace(const int& id); void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos); void moveTo(const Vector2D& pos);
Vector2D middle(); Vector2D middle();
void updateMatrix(); void updateMatrix();
int64_t activeWorkspaceID(); WORKSPACEID activeWorkspaceID();
int64_t activeSpecialWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox(); CBox logicalBox();
void scheduleDone(); void scheduleDone();
bool attemptDirectScanout(); bool attemptDirectScanout();
void setCTM(const Mat3x3& ctm);
void debugLastPresentation(const std::string& message);
bool m_bEnabled = false; bool m_bEnabled = false;
bool m_bRenderingInitPassed = false; bool m_bRenderingInitPassed = false;
@ -190,9 +198,9 @@ class CMonitor {
private: private:
void setupDefaultWS(const SMonitorRule&); void setupDefaultWS(const SMonitorRule&);
int findAvailableDefaultWS(); WORKSPACEID findAvailableDefaultWS();
wl_event_source* doneSource = nullptr; bool doneScheduled = false;
struct { struct {
CHyprSignalListener frame; CHyprSignalListener frame;

View file

@ -8,6 +8,8 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <cstring>
namespace Systemd { namespace Systemd {
int SdBooted(void) { int SdBooted(void) {

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