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

View file

@ -1,3 +1,5 @@
name: Nix (Build)
on:
workflow_call:
secrets:
@ -10,21 +12,17 @@ jobs:
matrix:
package:
- hyprland
- hyprland-cross
- xdg-desktop-portal-hyprland
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
submodules: recursive
- uses: cachix/install-nix-action@v27
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: cachix/cachix-action@v15
with:
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]
jobs:
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
secrets: inherit
build:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) && !contains(needs.*.result, 'failure')
needs: update-inputs
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
uses: ./.github/workflows/nix-build.yml
secrets: inherit

View file

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

View file

@ -20,7 +20,6 @@ jobs:
run: |
git fetch --unshallow || echo "failed unshallowing"
bash -c scripts/generateVersion.sh
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak
- name: Create tarball with submodules
id: tar

View file

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

6
.gitignore vendored
View file

@ -14,6 +14,7 @@ _deps
build/
result*
/.pre-commit-config.yaml
/.vscode/
/.idea/
.envrc
@ -37,3 +38,8 @@ gmon.out
PKGBUILD
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
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
@ -25,8 +25,18 @@ message(STATUS "Gathering git info")
execute_process(COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# udis
add_subdirectory("subprojects/udis86")
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")
include_directories("subprojects/udis86")
message(STATUS "udis86 dependency not found, falling back to subproject")
endif()
if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
@ -47,11 +57,11 @@ else()
set(BUILDTYPE_LOWER "release")
endif()
find_package(PkgConfig REQUIRED)
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
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)
message(STATUS "Configuring Hyprland in Debug with CMake")
@ -61,8 +71,11 @@ else()
message(STATUS "Configuring Hyprland in Release with CMake")
endif()
include_directories(. "src/" "subprojects/udis86/" "protocols/")
set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
include_directories(. "src/" "protocols/")
set(CMAKE_CXX_STANDARD 26)
add_compile_options(
-Wall
-Wextra
@ -87,16 +100,19 @@ else()
endif()
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(
deps
REQUIRED
IMPORTED_TARGET
aquamarine
xkbcommon
uuid
wayland-server
wayland-client
wayland-cursor
wayland-server>=1.22.90
wayland-protocols
cairo
pango
@ -105,15 +121,11 @@ pkg_check_modules(
xcursor
libdrm
libinput
hwdata
libseat
libdisplay-info
libliftoff
libudev
gbm
gio-2.0
hyprlang>=0.3.2
hyprcursor>=0.1.7
hyprutils>=0.2.1)
hyprutils>=0.2.3)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@ -195,14 +207,11 @@ else()
REQUIRED
IMPORTED_TARGET
xcb
xwayland
xcb-util
xcb-render
xcb-xfixes
xcb-icccm
xcb-composite
xcb-res
xcb-ewmh
xcb-errors)
target_link_libraries(Hyprland PkgConfig::xdeps)
endif()
@ -225,14 +234,19 @@ target_precompile_headers(Hyprland PRIVATE
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
add_custom_target(generate-protocol-headers)
function(protocolnew protoPath protoName external)
if(external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
set(path ${protoPath})
else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif()
@ -251,22 +265,31 @@ function(protocolWayland)
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums
${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
COMMAND
hyprwayland-scanner --wayland-enums
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
endfunction()
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads
libudis86 uuid)
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
protocolnew("subprojects/hyprland-protocols/protocols"
"hyprland-global-shortcuts-v1" true)
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.4.0)
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("subprojects/hyprland-protocols/protocols"
"hyprland-toplevel-export-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1"
true)
protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
protocolnew("protocols" "wlr-gamma-control-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" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1"
true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" 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/fractional-scale" "fractional-scale-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("staging/drm-lease" "drm-lease-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()
@ -323,19 +350,21 @@ install(
CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
${CMAKE_INSTALL_FULL_BINDIR}/hyprland
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
)")
# session file
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
# allow Hyprland to find wallpapers
# allow Hyprland to find assets
add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
# wallpapers
file(GLOB_RECURSE WALLPAPERS "assets/wall*")
install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
# installable assets
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
install(FILES ${INSTALLABLE_ASSETS}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
# default config
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf

View file

@ -1,28 +1,24 @@
PREFIX = /usr/local
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 --build ./build --config Release --target all
chmod -R 777 ./build
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 -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
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 --build ./build --config Release --target all
chmod -R 777 ./build
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 -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
release:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all
chmod -R 777 ./build
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 -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
debug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all
chmod -R 777 ./build
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 -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
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 --build ./build --config Release --target all
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 -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
clear:
rm -rf build
@ -87,8 +83,8 @@ asan:
#git reset --hard
@echo -en "If you want to apply a patch, input its path (leave empty for none):\n"
@read patchvar
@if [-n "$patchvar"]; then patch -p1 < $patchvar || echo ""; else echo "No patch specified"; fi
@read patchvar; \
if [ -n "$$patchvar" ]; then patch -p1 < "$$patchvar" || echo ""; else echo "No patch specified"; fi
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 ..

View file

@ -1,6 +1,6 @@
<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>
@ -125,7 +125,6 @@ easy IPC, much more QoL stuff than other compositors and more...
<!----------------------------------{ Images }--------------------------------->
[Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg
[Preview A]: https://i.ibb.co/C1yTb0r/falf.png
[Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.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
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')
subdir('install')

View file

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

View file

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

View file

@ -1,2 +1,2 @@
install_man ('Hyprland.1')
install_man ('hyprctl.1')
install_man('Hyprland.1')
install_man('hyprctl.1')

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

@ -59,7 +59,7 @@ env = HYPRCURSOR_SIZE,24
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
general {
gaps_in = 5
gaps_out = 20
@ -70,7 +70,7 @@ general {
col.inactive_border = rgba(595959aa)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
resize_on_border = false
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
@ -96,7 +96,7 @@ decoration {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
}
@ -117,6 +117,13 @@ animations {
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
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@ -129,7 +136,7 @@ master {
}
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}
@ -169,9 +176,9 @@ device {
}
####################
### KEYBINDINGSS ###
####################
###################
### KEYBINDINGS ###
###################
# See https://wiki.hyprland.org/Configuring/Keywords/
$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: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 ###
@ -242,4 +262,8 @@ bindm = $mainMod, mouse:273, resizewindow
# Example windowrule v2
# 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

@ -2,4 +2,6 @@
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
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('hyprland.desktop', install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), install_tag: 'runtime')
install_data(
'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": {
"lastModified": 1722347739,
"narHash": "sha256-rAoh+K6KG+b1DwSWtqRVocdojnH6nGk6q07mNltoUSM=",
"lastModified": 1728902391,
"narHash": "sha256-44bnoY0nAvbBQ/lVjmn511yL39Sv7SknV0BDxn34P3Q=",
"owner": "hyprwm",
"repo": "aquamarine",
"rev": "7c3565f9bedc7cb601cc0baa14792247e4dc1d5a",
"rev": "9874e08eec85b5542ca22494e127b0cdce46b786",
"type": "github"
},
"original": {
@ -29,6 +29,43 @@
"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": {
"inputs": {
"hyprlang": [
@ -42,11 +79,11 @@
]
},
"locked": {
"lastModified": 1721330371,
"narHash": "sha256-aYlHTWylczLt6ERJyg6E66Y/XSCbVL7leVcRuJmVbpI=",
"lastModified": 1728669738,
"narHash": "sha256-EDNAU9AYcx8OupUzbTbWE1d3HYdeG0wO6Msg3iL1muk=",
"owner": "hyprwm",
"repo": "hyprcursor",
"rev": "4493a972b48f9c3014befbbf381ed5fff91a65dc",
"rev": "0264e698149fcb857a66a53018157b41f8d97bb0",
"type": "github"
},
"original": {
@ -58,20 +95,18 @@
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
"xdph",
"nixpkgs"
],
"systems": [
"xdph",
"systems"
]
},
"locked": {
"lastModified": 1721326555,
"narHash": "sha256-zCu4R0CSHEactW9JqYki26gy8h9f6rHmSwj4XJmlHgg=",
"lastModified": 1728345020,
"narHash": "sha256-xGbkc7U/Roe0/Cv3iKlzijIaFBNguasI31ynL2IlEoM=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "5a11232266bf1a1f5952d5b179c3f4b2facaaa84",
"rev": "a7c183800e74f337753de186522b9017a07a8cee",
"type": "github"
},
"original": {
@ -93,11 +128,11 @@
]
},
"locked": {
"lastModified": 1721324361,
"narHash": "sha256-BiJKO0IIdnSwHQBSrEJlKlFr753urkLE48wtt0UhNG4=",
"lastModified": 1728168612,
"narHash": "sha256-AnB1KfiXINmuiW7BALYrKqcjCnsLZPifhb/7BsfPbns=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "adbefbf49664a6c2c8bf36b6487fd31e3eb68086",
"rev": "f054f2e44d6a0b74607a6bc0f52dba337a3db38e",
"type": "github"
},
"original": {
@ -116,11 +151,11 @@
]
},
"locked": {
"lastModified": 1722098849,
"narHash": "sha256-D3wIZlBNh7LuZ0NaoCpY/Pvu+xHxIVtSN+KkWZYvvVs=",
"lastModified": 1728941256,
"narHash": "sha256-WRypmcZ2Bw94lLmcmxYokVOHPJSZ7T06V49QZ4tkZeQ=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "5dcbbc1e3de40b2cecfd2007434d86e924468f1f",
"rev": "fd4be8b9ca932f7384e454bcd923c5451ef2aa85",
"type": "github"
},
"original": {
@ -139,11 +174,11 @@
]
},
"locked": {
"lastModified": 1721324119,
"narHash": "sha256-SOOqIT27/X792+vsLSeFdrNTF+OSRp5qXv6Te+fb2Qg=",
"lastModified": 1726874836,
"narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "a048a6cb015340bd82f97c1f40a4b595ca85cc30",
"rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
"type": "github"
},
"original": {
@ -154,11 +189,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1722185531,
"narHash": "sha256-veKR07psFoJjINLC8RK4DiLniGGMgF3QMlS4tb74S6k=",
"lastModified": 1728888510,
"narHash": "sha256-nsNdSldaAyu6PE3YUA+YQLqUDJh+gRbBooMMekZJwvI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "52ec9ac3b12395ad677e8b62106f0b98c1f8569d",
"rev": "a3c0b3b21515f74fd2665903d4ce6bc4dc81c77c",
"type": "github"
},
"original": {
@ -168,14 +203,55 @@
"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": {
"inputs": {
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems",
"xdph": "xdph"
}
@ -197,10 +273,18 @@
},
"xdph": {
"inputs": {
"hyprland-protocols": "hyprland-protocols",
"hyprland-protocols": [
"hyprland-protocols"
],
"hyprlang": [
"hyprlang"
],
"hyprutils": [
"hyprutils"
],
"hyprwayland-scanner": [
"hyprwayland-scanner"
],
"nixpkgs": [
"nixpkgs"
],
@ -209,11 +293,11 @@
]
},
"locked": {
"lastModified": 1722365976,
"narHash": "sha256-Khdm+mDzYA//XaU0M+hftod+rKr5q9SSHSEuiQ0/9ow=",
"lastModified": 1728166987,
"narHash": "sha256-w6dVTguAn9zJ+7aPOhBQgDz8bn6YZ7b56cY8Kg5HJRI=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "7f2a77ddf60390248e2a3de2261d7102a13e5341",
"rev": "fb9c8d665af0588bb087f97d0f673ddf0d501787",
"type": "github"
},
"original": {

View file

@ -22,6 +22,12 @@
inputs.hyprlang.follows = "hyprlang";
};
hyprland-protocols = {
url = "github:hyprwm/hyprland-protocols";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprlang = {
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
@ -45,7 +51,15 @@
url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
inputs.hyprland-protocols.follows = "hyprland-protocols";
inputs.hyprlang.follows = "hyprlang";
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
];
});
pkgsCrossFor = eachSystem (system: crossSystem:
import nixpkgs {
localSystem = system;
inherit crossSystem;
overlays = with self.overlays; [
hyprland-packages
hyprland-extras
];
});
in {
overlays = import ./nix/overlays.nix {inherit self lib inputs;};
@ -74,6 +97,18 @@
self.packages.${system})
// {
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: {
@ -81,34 +116,32 @@
inherit
(pkgsFor.${system})
# hyprland-packages
hyprland
hyprland-debug
hyprland-legacy-renderer
hyprland-unwrapped
# hyprland-extras
xdg-desktop-portal-hyprland
;
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
});
devShells = eachSystem (system: {
default =
pkgsFor.${system}.mkShell.override {
stdenv = pkgsFor.${system}.gcc13Stdenv;
inherit (self.packages.${system}.default) stdenv;
} {
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [
expat
libxml2
];
hardeningDisable = ["fortify"];
inputsFrom = [pkgsFor.${system}.hyprland];
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;
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")
target_link_libraries(hyprctl PUBLIC PkgConfig::deps)
target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
# binary
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
#include <string_view>
const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help]
commands:

View file

@ -1,17 +1,17 @@
_hyprctl_cmd_2 () {
_hyprctl_cmd_1 () {
hyprctl monitors | awk '/Monitor/{ print $2 }'
}
_hyprctl_cmd_3 () {
hyprpm list | awk '/Plugin/{ print $4 }'
hyprctl clients | awk '/class/{print $2}'
}
_hyprctl_cmd_2 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
}
_hyprctl_cmd_0 () {
hyprctl clients | awk '/class/{ print $2 }'
}
_hyprctl_cmd_1 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
hyprpm list | awk '/Plugin/{print $4}'
}
_hyprctl () {
@ -23,25 +23,25 @@ _hyprctl () {
local 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
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[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[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[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[9]="([117]=20 [114]=16)"
literal_transitions[11]="([103]=2)"
literal_transitions[13]="([21]=1 [119]=1 [30]=1 [139]=1 [121]=1 [44]=1 [72]=1)"
literal_transitions[14]="([39]=2)"
literal_transitions[15]="([138]=2 [96]=2)"
literal_transitions[17]="([18]=2 [7]=2)"
literal_transitions[18]="([76]=19)"
literal_transitions[19]="([34]=4 [45]=4)"
literal_transitions[20]="([8]=2 [67]=2 [14]=2 [137]=2)"
declare -A match_anything_transitions
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[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[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[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[6]="([126]=2)"
literal_transitions[10]="([56]=2)"
literal_transitions[11]="([9]=2)"
literal_transitions[12]="([14]=19 [80]=22)"
literal_transitions[13]="([142]=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[15]="([86]=4 [6]=4 [109]=4 [61]=4 [77]=4 [54]=4 [62]=4)"
literal_transitions[16]="([40]=2 [44]=2)"
literal_transitions[17]="([7]=23)"
literal_transitions[18]="([31]=2 [149]=2)"
literal_transitions[19]="([95]=2 [16]=2 [115]=2 [20]=2)"
literal_transitions[20]="([106]=2 [82]=2 [127]=2 [1]=2 [83]=2)"
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
local state=0
@ -79,21 +79,9 @@ _hyprctl () {
done
local -a matches=()
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
local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions
@ -102,25 +90,38 @@ _hyprctl () {
for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"}
COMPREPLY+=("$completion ")
matches+=("$literal ")
fi
done
fi
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
local command_id=${commands[$state]}
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
if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item")
matches+=("$item")
fi
done
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
}

View file

@ -1,21 +1,21 @@
function _hyprctl_3
function _hyprctl_2
set 1 $argv[1]
hyprctl monitors | awk '/Monitor/{ print $2 }'
end
function _hyprctl_4
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
function _hyprctl_1
set 1 $argv[1]
hyprctl clients | awk '/class/{ print $2 }'
end
function _hyprctl_2
set 1 $argv[1]
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
hyprpm list | awk '/Plugin/{print $4}'
end
function _hyprctl
@ -29,145 +29,160 @@ function _hyprctl
set COMP_CWORD (count $COMP_WORDS)
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[1] "Focus the next window on a workspace"
set descriptions[3] "Get the current cursor pos in global layout coordinates"
set descriptions[5] "Rename a workspace"
set descriptions[7] "Focus the first window matching"
set descriptions[10] "Swap the focused window with the next window"
set descriptions[12] "Move the active window"
set descriptions[16] "List the layers"
set descriptions[18] "List active outputs with their properties"
set descriptions[20] "Get into a kill mode, where you can kill an app by clicking on it"
set descriptions[21] "Set the current window's floating state to false"
set descriptions[22] "ERROR"
set descriptions[23] "Focus a monitor"
set descriptions[24] "Swap the active window with another window"
set descriptions[25] "Move the active window out of a group"
set descriptions[26] "Send a notification using the built-in Hyprland notification system"
set descriptions[27] "Move the cursor to a specified position"
set descriptions[28] "Set the cursor theme and reloads the cursor manager"
set descriptions[29] "Set the hyprctl error string"
set descriptions[30] "Move the active workspace to a monitor"
set descriptions[31] "CONFUSED"
set descriptions[34] "Set a property of a window"
set descriptions[35] "Specify the Hyprland instance"
set descriptions[36] "Disable output"
set descriptions[37] "Toggle the current window's floating state"
set descriptions[38] "Get the list of defined workspace rules"
set descriptions[39] "Move the focused window to a workspace"
set descriptions[41] "Temporarily enable or disable binds:ignore_group_lock"
set descriptions[42] "List all workspaces with their properties"
set descriptions[43] "Swap the active window with the next or previous in a group"
set descriptions[44] "Close a specified window"
set descriptions[45] "WARNING"
set descriptions[46] "Specify the Hyprland instance"
set descriptions[47] "List all registered binds"
set descriptions[48] "Move the active window in a direction or to a monitor"
set descriptions[49] "Change the split ratio"
set descriptions[51] "Prohibit the active window from becoming or being inserted into group"
set descriptions[52] "Change the workspace"
set descriptions[53] "List all current config parsing errors"
set descriptions[54] "Toggle the current active window into a group"
set descriptions[55] "Get the config option status (values)"
set descriptions[58] "Close the active window"
set descriptions[59] "Pass the key to a specified window"
set descriptions[60] "List all decorations and their info"
set descriptions[61] "List all connected keyboards and mice"
set descriptions[62] "Switch focus from current to previously focused window"
set descriptions[63] "Change the current mapping group"
set descriptions[64] "Execute a Global Shortcut using the GlobalShortcuts portal"
set descriptions[66] "Force the renderer to reload all resources and outputs"
set descriptions[67] "Move a selected window"
set descriptions[69] "Print the Hyprland version: flags, commit and branch of build"
set descriptions[70] "Set all monitors' DPMS status"
set descriptions[71] "Resize the active window"
set descriptions[72] "Move the active window into a group"
set descriptions[73] "OK"
set descriptions[75] "Set the current window's floating state to true"
set descriptions[76] "Print tail of the log"
set descriptions[79] "List all layouts available (including plugin ones)"
set descriptions[80] "Move a workspace to a monitor"
set descriptions[81] "Execute a shell command"
set descriptions[83] "Modify the window stack order of the active or specified window"
set descriptions[84] "Toggle the focused window's internal fullscreen state"
set descriptions[86] "Issue a keyword to call a config keyword dynamically"
set descriptions[89] "Disable output"
set descriptions[90] "Pin a window"
set descriptions[91] "Allows adding/removing fake outputs to a specific backend"
set descriptions[93] "Toggle a special workspace on/off"
set descriptions[94] "Toggle the focused window's fullscreen state"
set descriptions[95] "Toggle the current window to always be opaque"
set descriptions[96] "Focus the requested workspace"
set descriptions[98] "Switch to the next window in a group"
set descriptions[99] "Output in JSON format"
set descriptions[100] "List all running Hyprland instances and their info"
set descriptions[101] "Execute a raw shell command"
set descriptions[102] "Exit the compositor with no questions asked"
set descriptions[103] "List all windows with their properties"
set descriptions[105] "Execute a batch of commands separated by ;"
set descriptions[106] "Dismiss all or up to amount of notifications"
set descriptions[108] "Set the xkb layout index for a keyboard"
set descriptions[109] "Move window doesnt switch to the workspace"
set descriptions[110] "Apply a tag to the window"
set descriptions[111] "Behave as moveintogroup"
set descriptions[112] "Refresh state after issuing the command"
set descriptions[113] "Move the focus in a direction"
set descriptions[114] "Focus the urgent window or the last window"
set descriptions[116] "Get the active workspace name and its properties"
set descriptions[117] "Issue a dispatch to call a keybind dispatcher with an arg"
set descriptions[119] "Center the active window"
set descriptions[120] "HINT"
set descriptions[121] "Interact with hyprpaper if present"
set descriptions[122] "No Icon"
set descriptions[123] "Force reload the config"
set descriptions[125] "Print system info"
set descriptions[126] "Interact with a plugin"
set descriptions[128] "Get the active window name and its properties"
set descriptions[129] "Swap the active workspaces between two monitors"
set descriptions[130] "Print the current random splash"
set descriptions[131] "On shortcut X sends shortcut Y to a specified window"
set descriptions[133] "Lock the focused group"
set descriptions[136] "Lock the groups"
set descriptions[137] "Move the cursor to the corner of the active window"
set descriptions[140] "INFO"
set descriptions[141] "Resize a selected window"
set descriptions
set descriptions[1] "Resize the active window"
set descriptions[2] "Fullscreen"
set descriptions[3] "Switch to the next window in a group"
set descriptions[4] "Refresh state after issuing the command"
set descriptions[5] "Move the active window into a group"
set descriptions[7] "CONFUSED"
set descriptions[9] "Print system info"
set descriptions[11] "List all layouts available (including plugin ones)"
set descriptions[12] "Set a property of a window"
set descriptions[14] "Set the xkb layout index for a keyboard"
set descriptions[16] "Prohibit the active window from becoming or being inserted into group"
set descriptions[19] "Execute a shell command"
set descriptions[20] "Set the cursor theme and reloads the cursor manager"
set descriptions[22] "Focus the urgent window or the last window"
set descriptions[23] "Get the list of defined workspace rules"
set descriptions[24] "Move the active workspace to a monitor"
set descriptions[25] "Move window doesnt switch to the workspace"
set descriptions[26] "Interact with hyprpaper if present"
set descriptions[29] "Swap the active window with the next or previous in a group"
set descriptions[30] "Move the cursor to the corner of the active window"
set descriptions[31] "Move a selected window"
set descriptions[33] "Move the active window in a direction or to a monitor"
set descriptions[34] "Lists all global shortcuts"
set descriptions[35] "List all windows with their properties"
set descriptions[37] "Temporarily enable or disable binds:ignore_group_lock"
set descriptions[38] "Print the current random splash"
set descriptions[39] "Execute a raw shell command"
set descriptions[40] "List active outputs with their properties"
set descriptions[43] "Disable output"
set descriptions[44] "Gets the current config info about animations and beziers"
set descriptions[47] "Change the split ratio"
set descriptions[48] "Move the active window"
set descriptions[49] "Pass the key to a specified window"
set descriptions[50] "Swap the focused window with the next window"
set descriptions[51] "List all connected keyboards and mice"
set descriptions[52] "List the layers"
set descriptions[54] "Lock the focused group"
set descriptions[55] "OK"
set descriptions[56] "Move a workspace to a monitor"
set descriptions[58] "Specify the Hyprland instance"
set descriptions[59] "Disable output"
set descriptions[61] "Pin a window"
set descriptions[62] "WARNING"
set descriptions[63] "INFO"
set descriptions[66] "Set the current window's floating state to true"
set descriptions[69] "On shortcut X sends shortcut Y to a specified window"
set descriptions[70] "List all workspaces with their properties"
set descriptions[71] "Focus the next window on a workspace"
set descriptions[72] "Modify the window stack order of the active or specified window"
set descriptions[73] "Toggle the current active window into a group"
set descriptions[74] "Lock the groups"
set descriptions[76] "Set all monitors' DPMS status"
set descriptions[77] "Switch focus from current to previously focused window"
set descriptions[78] "No Icon"
set descriptions[79] "Execute a batch of commands separated by ;"
set descriptions[80] "Send a notification using the built-in Hyprland notification system"
set descriptions[82] "List all running Hyprland instances and their info"
set descriptions[83] "Maximize no fullscreen"
set descriptions[84] "Maximize and fullscreen"
set descriptions[85] "Move the active window out of a group"
set descriptions[86] "Close the active window"
set descriptions[87] "HINT"
set descriptions[88] "Move the focused window to a workspace"
set descriptions[89] "Move the cursor to a specified position"
set descriptions[90] "List all current config parsing errors"
set descriptions[91] "Close a specified window"
set descriptions[92] "Swap the active window with another window"
set descriptions[93] "Apply a tag to the window"
set descriptions[94] "Force the renderer to reload all resources and outputs"
set descriptions[95] "Center the active window"
set descriptions[97] "Focus the first window matching"
set descriptions[98] "Set the hyprctl error string"
set descriptions[101] "List all registered binds"
set descriptions[102] "Print the Hyprland version: flags, commit and branch of build"
set descriptions[103] "Prints the help message"
set descriptions[104] "Toggle a special workspace on/off"
set descriptions[105] "Toggle the focused window's fullscreen state"
set descriptions[107] "None"
set descriptions[108] "Issue a keyword to call a config keyword dynamically"
set descriptions[109] "Toggle the current window to always be opaque"
set descriptions[110] "ERROR"
set descriptions[111] "Specify the Hyprland instance"
set descriptions[112] "Toggle the current window's floating state"
set descriptions[113] "Rename a workspace"
set descriptions[115] "Get the active workspace name and its properties"
set descriptions[117] "Get into a kill mode, where you can kill an app by clicking on it"
set descriptions[119] "Allows adding/removing fake outputs to a specific backend"
set descriptions[120] "Execute a Global Shortcut using the GlobalShortcuts portal"
set descriptions[121] "Issue a dispatch to call a keybind dispatcher with an arg"
set descriptions[122] "Force reload the config"
set descriptions[124] "Output in JSON format"
set descriptions[125] "Emits a custom event to socket2"
set descriptions[126] "Prints the help message"
set descriptions[128] "Current"
set descriptions[129] "Get the active window name and its properties"
set descriptions[131] "Dismiss all or up to amount of notifications"
set descriptions[132] "Focus a monitor"
set descriptions[133] "Move the focus in a direction"
set descriptions[134] "Interact with a plugin"
set descriptions[135] "Exit the compositor with no questions asked"
set descriptions[136] "Change the workspace"
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[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[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[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[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[10] "set inputs 118 115; set tos 21 17"
set literal_transitions[12] "set inputs 104; 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[15] "set inputs 40; set tos 3"
set literal_transitions[16] "set inputs 139 97; set tos 3 3"
set literal_transitions[18] "set inputs 19 8; set tos 3 3"
set literal_transitions[19] "set inputs 77; set tos 20"
set literal_transitions[20] "set inputs 35 46; set tos 5 5"
set literal_transitions[21] "set inputs 9 68 15 138; set tos 3 3 3 3"
set literal_transitions
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[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[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[7] "set inputs 127; set tos 3"
set literal_transitions[11] "set inputs 57; set tos 3"
set literal_transitions[12] "set inputs 10; set tos 3"
set literal_transitions[13] "set inputs 15 81; set tos 20 23"
set literal_transitions[14] "set inputs 143; set tos 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[16] "set inputs 87 7 110 62 78 55 63; set tos 5 5 5 5 5 5 5"
set literal_transitions[17] "set inputs 41 45; set tos 3 3"
set literal_transitions[18] "set inputs 8; set tos 24"
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 --local match_anything_transitions_to 3 8 3 3 3 16 19 8 19 3 3 19
set match_anything_transitions_from 7 8 1 23 6 5 3 19 12 9 10 14 11 2
set match_anything_transitions_to 18 3 2 3 19 3 18 3 18 3 3 18 18 2
set --local state 1
set --local word_index 2
set state 1
set word_index 2
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]
set --local --erase inputs
set --local --erase tos
set --erase inputs
set --erase tos
eval $literal_transitions[$state]
if contains -- $word $literals
set --local literal_matched 0
set literal_matched 0
for literal_id in (seq 1 (count $literals))
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 word_index (math $word_index + 1)
set literal_matched 1
@ -181,7 +196,7 @@ function _hyprctl
end
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 word_index (math $word_index + 1)
continue
@ -191,8 +206,8 @@ function _hyprctl
end
if set --query literal_transitions[$state] && test -n $literal_transitions[$state]
set --local --erase inputs
set --local --erase tos
set --erase inputs
set --erase tos
eval $literal_transitions[$state]
for literal_id in $inputs
if test -n $descriptions[$literal_id]
@ -203,14 +218,14 @@ function _hyprctl
end
end
set command_states 6 17 13 11
set command_ids 2 3 4 1
set command_states 8 23 9 6
set command_ids 1 2 4 3
if contains $state $command_states
set --local index (contains --index $state $command_states)
set --local function_id $command_ids[$index]
set --local function_name _hyprctl_$function_id
set --local --erase inputs
set --local --erase tos
set index (contains --index $state $command_states)
set function_id $command_ids[$index]
set function_name _hyprctl_$function_id
set --erase inputs
set --erase tos
$function_name "$COMP_WORDS[$COMP_CWORD]"
end

View file

@ -2,13 +2,14 @@
# Repo: https://github.com/adaszko/complgen
# Generate completion scripts: "complgen aot --bash-script hyprctl.bash --fish-script hyprctl.fish --zsh-script hyprctl.zsh ./hyprctl.usage"
hyprctl [<OPTIONS>]... <ARGUMENTS>
hyprctl [<OPTIONS>]... <ARGUMENTS>
<OPTIONS> ::= (-i | --instance) "Specify the Hyprland instance"
| (-j) "Output in JSON format"
| (-r) "Refresh state after issuing the command"
| (--batch) "Execute a batch of commands separated by ;"
| (-q | --quiet) "Disable output"
| (-h | --help) "Prints the help message"
;
<WINDOWS> ::= {{{ hyprctl clients | awk '/class/{print $2}' }}};
@ -59,16 +60,18 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
<ARGUMENTS> ::= (activewindow) "Get the active window 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"
| (clients) "List all windows with their properties"
| (configerrors) "List all current config parsing errors"
| (cursorpos) "Get the current cursor pos in global layout coordinates"
| (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"
| (dismissnotify <NUM>) "Dismiss all or up to amount of notifications"
| (dispatch <DISPATCHERS>) "Issue a dispatch to call a keybind dispatcher with an arg"
| (getoption) "Get the config option status (values)"
| (globalshortcuts) ""
| (globalshortcuts) "Lists all global shortcuts"
| (hyprpaper) "Interact with hyprpaper if present"
| (instances) "List all running Hyprland instances and their info"
| (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"
| (output (create (wayland | x11 | headless | auto) | remove <MONITORS>)) "Allows adding/removing fake outputs to a specific backend"
| (plugin <AVAILABLE_PLUGINS>) "Interact with a plugin"
| (reload) "Force reload the config"
| (rollinglog) "Print tail of the log"
| (reload [config-only]) "Force reload the config"
| (rollinglog [-f]) "Print tail of the log"
| (setcursor) "Set the cursor theme and reloads the cursor manager"
| (seterror [disable]) "Set the hyprctl error string"
| (setprop <PROPS>) "Set a property of a window"
@ -92,6 +95,13 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (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"
| (execr) "Execute a raw shell command"
| (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"
| (fullscreen) "Toggle the focused window's 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"
| (pin) "Pin a window"
| (movefocus) "Move the focus in a direction"
@ -148,4 +159,5 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
| (setignoregrouplock) "Temporarily enable or disable binds:ignore_group_lock"
| (global) "Execute a Global Shortcut using the GlobalShortcuts portal"
| (submap) "Change the current mapping group"
| (event) "Emits a custom event to socket2"
;

View file

@ -1,145 +1,160 @@
#compdef hyprctl
_hyprctl_cmd_2 () {
_hyprctl_cmd_1 () {
hyprctl monitors | awk '/Monitor/{ print $2 }'
}
_hyprctl_cmd_3 () {
hyprpm list | awk '/Plugin/{ print $4 }'
hyprctl clients | awk '/class/{print $2}'
}
_hyprctl_cmd_0 () {
hyprctl clients | awk '/class/{ print $2 }'
}
_hyprctl_cmd_1 () {
_hyprctl_cmd_2 () {
hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}'
}
_hyprctl_cmd_0 () {
hyprpm list | awk '/Plugin/{print $4}'
}
_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
descriptions[1]="Focus the next window on a workspace"
descriptions[3]="Get the current cursor pos in global layout coordinates"
descriptions[5]="Rename a workspace"
descriptions[7]="Focus the first window matching"
descriptions[10]="Swap the focused window with the next window"
descriptions[12]="Move the active window"
descriptions[16]="List the layers"
descriptions[18]="List active outputs with their properties"
descriptions[20]="Get into a kill mode, where you can kill an app by clicking on it"
descriptions[21]="Set the current window's floating state to false"
descriptions[22]="ERROR"
descriptions[23]="Focus a monitor"
descriptions[24]="Swap the active window with another window"
descriptions[25]="Move the active window out of a group"
descriptions[26]="Send a notification using the built-in Hyprland notification system"
descriptions[27]="Move the cursor to a specified position"
descriptions[28]="Set the cursor theme and reloads the cursor manager"
descriptions[29]="Set the hyprctl error string"
descriptions[30]="Move the active workspace to a monitor"
descriptions[31]="CONFUSED"
descriptions[34]="Set a property of a window"
descriptions[35]="Specify the Hyprland instance"
descriptions[36]="Disable output"
descriptions[37]="Toggle the current window's floating state"
descriptions[38]="Get the list of defined workspace rules"
descriptions[39]="Move the focused window to a workspace"
descriptions[41]="Temporarily enable or disable binds:ignore_group_lock"
descriptions[42]="List all workspaces with their properties"
descriptions[43]="Swap the active window with the next or previous in a group"
descriptions[44]="Close a specified window"
descriptions[45]="WARNING"
descriptions[46]="Specify the Hyprland instance"
descriptions[47]="List all registered binds"
descriptions[48]="Move the active window in a direction or to a monitor"
descriptions[49]="Change the split ratio"
descriptions[51]="Prohibit the active window from becoming or being inserted into group"
descriptions[52]="Change the workspace"
descriptions[53]="List all current config parsing errors"
descriptions[54]="Toggle the current active window into a group"
descriptions[55]="Get the config option status (values)"
descriptions[58]="Close the active window"
descriptions[59]="Pass the key to a specified window"
descriptions[60]="List all decorations and their info"
descriptions[61]="List all connected keyboards and mice"
descriptions[62]="Switch focus from current to previously focused window"
descriptions[63]="Change the current mapping group"
descriptions[64]="Execute a Global Shortcut using the GlobalShortcuts portal"
descriptions[66]="Force the renderer to reload all resources and outputs"
descriptions[67]="Move a selected window"
descriptions[69]="Print the Hyprland version: flags, commit and branch of build"
descriptions[70]="Set all monitors' DPMS status"
descriptions[71]="Resize the active window"
descriptions[72]="Move the active window into a group"
descriptions[73]="OK"
descriptions[75]="Set the current window's floating state to true"
descriptions[76]="Print tail of the log"
descriptions[79]="List all layouts available (including plugin ones)"
descriptions[80]="Move a workspace to a monitor"
descriptions[81]="Execute a shell command"
descriptions[83]="Modify the window stack order of the active or specified window"
descriptions[84]="Toggle the focused window's internal fullscreen state"
descriptions[86]="Issue a keyword to call a config keyword dynamically"
descriptions[89]="Disable output"
descriptions[90]="Pin a window"
descriptions[91]="Allows adding/removing fake outputs to a specific backend"
descriptions[93]="Toggle a special workspace on/off"
descriptions[94]="Toggle the focused window's fullscreen state"
descriptions[95]="Toggle the current window to always be opaque"
descriptions[96]="Focus the requested workspace"
descriptions[98]="Switch to the next window in a group"
descriptions[99]="Output in JSON format"
descriptions[100]="List all running Hyprland instances and their info"
descriptions[101]="Execute a raw shell command"
descriptions[102]="Exit the compositor with no questions asked"
descriptions[103]="List all windows with their properties"
descriptions[105]="Execute a batch of commands separated by ;"
descriptions[106]="Dismiss all or up to amount of notifications"
descriptions[108]="Set the xkb layout index for a keyboard"
descriptions[109]="Move window doesnt switch to the workspace"
descriptions[110]="Apply a tag to the window"
descriptions[111]="Behave as moveintogroup"
descriptions[112]="Refresh state after issuing the command"
descriptions[113]="Move the focus in a direction"
descriptions[114]="Focus the urgent window or the last window"
descriptions[116]="Get the active workspace name and its properties"
descriptions[117]="Issue a dispatch to call a keybind dispatcher with an arg"
descriptions[119]="Center the active window"
descriptions[120]="HINT"
descriptions[121]="Interact with hyprpaper if present"
descriptions[122]="No Icon"
descriptions[123]="Force reload the config"
descriptions[125]="Print system info"
descriptions[126]="Interact with a plugin"
descriptions[128]="Get the active window name and its properties"
descriptions[129]="Swap the active workspaces between two monitors"
descriptions[130]="Print the current random splash"
descriptions[131]="On shortcut X sends shortcut Y to a specified window"
descriptions[133]="Lock the focused group"
descriptions[136]="Lock the groups"
descriptions[137]="Move the cursor to the corner of the active window"
descriptions[140]="INFO"
descriptions[141]="Resize a selected window"
descriptions[1]="Resize the active window"
descriptions[2]="Fullscreen"
descriptions[3]="Switch to the next window in a group"
descriptions[4]="Refresh state after issuing the command"
descriptions[5]="Move the active window into a group"
descriptions[7]="CONFUSED"
descriptions[9]="Print system info"
descriptions[11]="List all layouts available (including plugin ones)"
descriptions[12]="Set a property of a window"
descriptions[14]="Set the xkb layout index for a keyboard"
descriptions[16]="Prohibit the active window from becoming or being inserted into group"
descriptions[19]="Execute a shell command"
descriptions[20]="Set the cursor theme and reloads the cursor manager"
descriptions[22]="Focus the urgent window or the last window"
descriptions[23]="Get the list of defined workspace rules"
descriptions[24]="Move the active workspace to a monitor"
descriptions[25]="Move window doesnt switch to the workspace"
descriptions[26]="Interact with hyprpaper if present"
descriptions[29]="Swap the active window with the next or previous in a group"
descriptions[30]="Move the cursor to the corner of the active window"
descriptions[31]="Move a selected window"
descriptions[33]="Move the active window in a direction or to a monitor"
descriptions[34]="Lists all global shortcuts"
descriptions[35]="List all windows with their properties"
descriptions[37]="Temporarily enable or disable binds:ignore_group_lock"
descriptions[38]="Print the current random splash"
descriptions[39]="Execute a raw shell command"
descriptions[40]="List active outputs with their properties"
descriptions[43]="Disable output"
descriptions[44]="Gets the current config info about animations and beziers"
descriptions[47]="Change the split ratio"
descriptions[48]="Move the active window"
descriptions[49]="Pass the key to a specified window"
descriptions[50]="Swap the focused window with the next window"
descriptions[51]="List all connected keyboards and mice"
descriptions[52]="List the layers"
descriptions[54]="Lock the focused group"
descriptions[55]="OK"
descriptions[56]="Move a workspace to a monitor"
descriptions[58]="Specify the Hyprland instance"
descriptions[59]="Disable output"
descriptions[61]="Pin a window"
descriptions[62]="WARNING"
descriptions[63]="INFO"
descriptions[66]="Set the current window's floating state to true"
descriptions[69]="On shortcut X sends shortcut Y to a specified window"
descriptions[70]="List all workspaces with their properties"
descriptions[71]="Focus the next window on a workspace"
descriptions[72]="Modify the window stack order of the active or specified window"
descriptions[73]="Toggle the current active window into a group"
descriptions[74]="Lock the groups"
descriptions[76]="Set all monitors' DPMS status"
descriptions[77]="Switch focus from current to previously focused window"
descriptions[78]="No Icon"
descriptions[79]="Execute a batch of commands separated by ;"
descriptions[80]="Send a notification using the built-in Hyprland notification system"
descriptions[82]="List all running Hyprland instances and their info"
descriptions[83]="Maximize no fullscreen"
descriptions[84]="Maximize and fullscreen"
descriptions[85]="Move the active window out of a group"
descriptions[86]="Close the active window"
descriptions[87]="HINT"
descriptions[88]="Move the focused window to a workspace"
descriptions[89]="Move the cursor to a specified position"
descriptions[90]="List all current config parsing errors"
descriptions[91]="Close a specified window"
descriptions[92]="Swap the active window with another window"
descriptions[93]="Apply a tag to the window"
descriptions[94]="Force the renderer to reload all resources and outputs"
descriptions[95]="Center the active window"
descriptions[97]="Focus the first window matching"
descriptions[98]="Set the hyprctl error string"
descriptions[101]="List all registered binds"
descriptions[102]="Print the Hyprland version: flags, commit and branch of build"
descriptions[103]="Prints the help message"
descriptions[104]="Toggle a special workspace on/off"
descriptions[105]="Toggle the focused window's fullscreen state"
descriptions[107]="None"
descriptions[108]="Issue a keyword to call a config keyword dynamically"
descriptions[109]="Toggle the current window to always be opaque"
descriptions[110]="ERROR"
descriptions[111]="Specify the Hyprland instance"
descriptions[112]="Toggle the current window's floating state"
descriptions[113]="Rename a workspace"
descriptions[115]="Get the active workspace name and its properties"
descriptions[117]="Get into a kill mode, where you can kill an app by clicking on it"
descriptions[119]="Allows adding/removing fake outputs to a specific backend"
descriptions[120]="Execute a Global Shortcut using the GlobalShortcuts portal"
descriptions[121]="Issue a dispatch to call a keybind dispatcher with an arg"
descriptions[122]="Force reload the config"
descriptions[124]="Output in JSON format"
descriptions[125]="Emits a custom event to socket2"
descriptions[126]="Prints the help message"
descriptions[128]="Current"
descriptions[129]="Get the active window name and its properties"
descriptions[131]="Dismiss all or up to amount of notifications"
descriptions[132]="Focus a monitor"
descriptions[133]="Move the focus in a direction"
descriptions[134]="Interact with a plugin"
descriptions[135]="Exit the compositor with no questions asked"
descriptions[136]="Change the workspace"
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
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[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[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[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[10]="([118]=21 [115]=17)"
literal_transitions[12]="([104]=3)"
literal_transitions[14]="([22]=2 [120]=2 [31]=2 [140]=2 [122]=2 [45]=2 [73]=2)"
literal_transitions[15]="([40]=3)"
literal_transitions[16]="([139]=3 [97]=3)"
literal_transitions[18]="([19]=3 [8]=3)"
literal_transitions[19]="([77]=20)"
literal_transitions[20]="([35]=5 [46]=5)"
literal_transitions[21]="([9]=3 [68]=3 [15]=3 [138]=3)"
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[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[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[7]="([127]=3)"
literal_transitions[11]="([57]=3)"
literal_transitions[12]="([10]=3)"
literal_transitions[13]="([15]=20 [81]=23)"
literal_transitions[14]="([143]=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[16]="([87]=5 [7]=5 [110]=5 [62]=5 [78]=5 [55]=5 [63]=5)"
literal_transitions[17]="([41]=3 [45]=3)"
literal_transitions[18]="([8]=24)"
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
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
@ -199,7 +214,7 @@ _hyprctl () {
fi
done
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
local command_id=${commands[$state]}
@ -252,4 +267,8 @@ _hyprctl () {
return 0
}
compdef _hyprctl hyprctl
if [[ $ZSH_EVAL_CONTEXT =~ :file$ ]]; then
compdef _hyprctl hyprctl
else
_hyprctl
fi

View file

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

View file

@ -1,10 +1,26 @@
executable('hyprctl', 'main.cpp',
executable(
'hyprctl',
'main.cpp',
dependencies: [
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('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')
install_data(
'hyprctl.bash',
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_cmd_1 () {
hyprpm list | awk '/Repository/{print $4}' | sed 's/:$//'
}
_hyprpm () {
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
@ -11,16 +15,13 @@ _hyprpm () {
local 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
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[1]="([10]=2 [11]=3 [3]=3 [2]=2 [14]=2 [5]=2 [6]=2)"
literal_transitions[4]="([1]=5)"
literal_transitions[5]="([0]=6 [12]=6)"
declare -A match_anything_transitions
match_anything_transitions=([3]=2 [2]=4 [0]=1 [1]=1)
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]="([12]=2 [13]=3 [3]=3 [4]=4 [16]=4 [6]=4 [7]=4)"
literal_transitions[5]="([2]=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 subword_transitions
local state=0
@ -58,21 +59,9 @@ _hyprpm () {
done
local -a matches=()
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
local state_transitions_initializer=${literal_transitions[$state]}
declare -A state_transitions
@ -81,25 +70,38 @@ _hyprpm () {
for literal_id in "${!state_transitions[@]}"; do
local literal="${literals[$literal_id]}"
if [[ $literal = "${prefix}"* ]]; then
local completion=${literal#"$superfluous_prefix"}
COMPREPLY+=("$completion ")
matches+=("$literal ")
fi
done
fi
declare -A commands
commands=([3]=0)
commands=([3]=0 [2]=1)
if [[ -v "commands[$state]" ]]; then
local command_id=${commands[$state]}
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
if [[ $item = "${prefix}"* ]]; then
COMPREPLY+=("$item")
matches+=("$item")
fi
done
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
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,15 @@
#include "PluginManager.hpp"
#include "../helpers/Colors.hpp"
#include "../helpers/StringUtils.hpp"
#include "../progress/CProgressBar.hpp"
#include "Manifest.hpp"
#include "DataState.hpp"
#include <cstdio>
#include <iostream>
#include <array>
#include <filesystem>
#include <print>
#include <thread>
#include <fstream>
#include <algorithm>
@ -31,6 +34,7 @@ static std::string execAndGet(std::string cmd) {
if (!pipe)
return "";
result.reserve(buffer.size());
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
@ -47,10 +51,10 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
once = true;
const auto HLVERCALL = execAndGet("hyprctl version");
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "version returned: " << HLVERCALL << "\n";
std::println("{}", verboseString("version returned: {}", HLVERCALL));
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{};
}
@ -76,7 +80,7 @@ SHyprlandVersion CPluginManager::getHyprlandVersion() {
} catch (...) { ; }
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};
return ver;
@ -102,20 +106,21 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HLVER = getHyprlandVersion();
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;
}
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;
}
auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) {
std::cout << Colors::YELLOW << "!" << Colors::RED << " Disclaimer:\n " << Colors::RESET
<< "plugins, especially not official, have no guarantee of stability, availablity or security.\n Run them at your own risk.\n "
<< "This message will not appear again.\n";
std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET);
std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n"
"Run them at your own risk.\n"
"This message will not appear again.");
GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE);
}
@ -129,7 +134,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
std::println(stderr, "Aborting.");
return false;
}
@ -144,7 +149,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::filesystem::create_directory("/tmp/hyprpm");
std::filesystem::permissions("/tmp/hyprpm", std::filesystem::perms::all, std::filesystem::perm_options::replace);
} 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;
}
@ -153,60 +158,60 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
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;
}
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);
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;
}
if (!rev.empty()) {
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev);
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;
}
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
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.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.printMessageAbove(successString("cloned"));
progress.m_szCurrentMessage = "Reading the manifest";
progress.print();
std::unique_ptr<CManifest> pManifest;
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");
} 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");
}
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;
}
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;
}
progress.m_iSteps = 2;
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:");
for (auto& pl : pManifest->m_vPlugins) {
std::string message = std::string{Colors::RESET} + " " + pl.name + " by ";
for (auto& a : pl.authors) {
progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
for (auto const& pl : pManifest->m_vPlugins) {
std::string message = "" + pl.name + " by ";
for (auto const& a : pl.authors) {
message += a + ", ";
}
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()) {
// 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)
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);
ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " submodule update --init");
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;
}
@ -244,12 +249,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
const auto HEADERSSTATUS = headersValid();
if (HEADERSSTATUS != HEADERS_OK) {
std::cerr << "\n" << headerError(HEADERSSTATUS);
std::println("\n{}", headerError(HEADERSSTATUS));
return false;
}
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.print();
@ -257,35 +262,36 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::string out;
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;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
for (auto const& bs : p.buildSteps) {
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
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)) {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " Plugin " + p.name + " 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 "
"first.\n Try re-running with -v to see "
"more verbose output.\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 first\n"
" Try re-running with -v to see more verbose output.\n",
p.name));
p.failed = true;
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_szCurrentMessage = "Installing repository";
progress.print();
@ -299,18 +305,18 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
repo.url = url;
repo.rev = rev;
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});
}
DataState::addNewPluginRepo(repo);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " installed repository");
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " you can now enable the plugin(s) with hyprpm enable");
progress.printMessageAbove(successString("installed repository"));
progress.printMessageAbove(successString("you can now enable the plugin(s) with hyprpm enable"));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
// remove build files
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) {
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;
}
@ -331,7 +337,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
std::getline(std::cin, input);
if (input.size() > 0 && input[0] != 'Y' && input[0] != 'y') {
std::cout << "Aborting.\n";
std::println("Aborting.");
return false;
}
@ -347,15 +353,15 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_MISSING;
// find headers commit
std::string cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd.c_str());
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());
if (!headers.contains("-I/"))
return HEADERS_MISSING;
headers.pop_back(); // pop newline
std::string verHeader = "";
std::string verHeader;
while (!headers.empty()) {
const auto PATH = headers.substr(0, headers.find(" -I/", 3));
@ -406,7 +412,7 @@ bool CPluginManager::updateHeaders(bool force) {
const auto HLVER = getHyprlandVersion();
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;
}
@ -416,7 +422,7 @@ bool CPluginManager::updateHeaders(bool force) {
}
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;
}
@ -430,74 +436,73 @@ bool CPluginManager::updateHeaders(bool force) {
const auto WORKINGDIR = "/tmp/hyprpm/hyprland-" + USERNAME;
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;
}
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
// due to timezones, etc.
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'");
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'");
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 =
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)) {
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);
}
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;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " cloned");
progress.printMessageAbove(successString("Hyprland cloned"));
progress.m_iSteps = 2;
progress.m_szCurrentMessage = "Checking out sources";
progress.print();
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");
if (ret.contains("fatal: unable to read tree")) {
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET
<< " 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";
std::println(stderr, "\n{}",
failureString("Could not checkout the running Hyprland commit. If you are on -git, try updating.\n"
"You can also try re-running hyprpm update with --no-shallow."));
return false;
}
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);
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_szCurrentMessage = "Building Hyprland";
progress.print();
progress.printMessageAbove(std::string{Colors::YELLOW} + "!" + Colors::RESET + " configuring Hyprland");
progress.printMessageAbove(statusString("!", Colors::YELLOW, "configuring Hyprland"));
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,
DataState::getHeadersPath()));
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")) {
// 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_last_of('\n'));
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n"
<< missing << "\n\nThis likely means that you are missing the above dependencies or they are out of date.\n";
std::println(stderr, "\n{}",
failureString("Could not configure the hyprland source, cmake complained:\n{}\n\n"
"This likely means that you are missing the above dependencies or they are out of date.",
missing));
return false;
}
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.printMessageAbove(successString("configured Hyprland"));
progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources";
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);
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);
if (m_bVerbose)
std::cout << Colors::BLUE << "[v] " << Colors::RESET << "installer returned: " << ret << "\n";
std::println("{}", verboseString("installer returned: {}", ret));
// remove build files
std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid();
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_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
} else {
progress.printMessageAbove(std::string{Colors::RED} + "" + Colors::RESET + " failed to install headers with error code " + std::to_string((int)HEADERSVALID) + " (" +
headerErrorShort(HEADERSVALID) + ")");
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
std::cout << "\n";
std::cerr << "\n" << headerError(HEADERSVALID);
std::print(stderr, "\n\n{}", headerError(HEADERSVALID));
return false;
}
@ -557,14 +560,14 @@ bool CPluginManager::updateHeaders(bool force) {
bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (headersValid() != HEADERS_OK) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
std::println("{}", failureString("headers are not up-to-date, please run hyprpm update."));
return false;
}
const auto REPOS = DataState::getAllRepositories();
if (REPOS.size() < 1) {
std::cout << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " No repos to update.\n";
std::println("{}", failureString("No repos to update."));
return true;
}
@ -579,32 +582,33 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
const std::string USERNAME = getpwuid(getuid())->pw_name;
m_szWorkingPluginDirectory = "/tmp/hyprpm/" + USERNAME;
for (auto& repo : REPOS) {
for (auto const& repo : REPOS) {
bool update = forceUpdateAll;
progress.m_iSteps++;
progress.m_szCurrentMessage = "Updating " + repo.name;
progress.print();
progress.printMessageAbove(std::string{Colors::RESET} + " → checking for updates for " + repo.name);
progress.printMessageAbove(infoString("checking for updates for {}", repo.name));
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);
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;
}
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);
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;
}
}
@ -620,7 +624,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!update) {
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.print();
continue;
@ -628,41 +632,41 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
// we need to update
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " repository " + repo.name + " has updates.");
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + repo.name);
progress.printMessageAbove(successString("repository {} has updates.", repo.name));
progress.printMessageAbove(infoString("Building {}", repo.name));
progress.m_iSteps++;
progress.print();
std::unique_ptr<CManifest> pManifest;
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");
} 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");
}
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;
}
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;
}
if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
// 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)
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);
}
@ -672,32 +676,33 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
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. */) {
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;
continue;
}
progress.printMessageAbove(std::string{Colors::RESET} + " → Building " + p.name);
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto& bs : p.buildSteps) {
std::string cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
for (auto const& bs : p.buildSteps) {
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
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)) {
std::cerr << "\n"
<< Colors::RED << "" << Colors::RESET << " Plugin " << p.name << " 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 first.\n Try "
"re-running with -v to see more verbose "
"output.\n";
std::println(stderr,
"\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 re-running with -v to see more verbose output.",
failureString("Plugin {} failed to build.", p.name));
p.failed = true;
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
@ -709,7 +714,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (repohash.length() > 0)
repohash.pop_back();
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; });
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);
progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " updated " + repo.name);
progress.printMessageAbove(successString("updated {}", repo.name));
}
progress.m_iSteps++;
@ -733,7 +738,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Done!";
progress.print();
std::cout << "\n";
std::print("\n");
return true;
}
@ -741,27 +746,27 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
bool CPluginManager::enablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, true);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Enabled " << name << "\n";
std::println("{}", successString("Enabled {}", name));
return ret;
}
bool CPluginManager::disablePlugin(const std::string& name) {
bool ret = DataState::setPluginEnabled(name, false);
if (ret)
std::cout << Colors::GREEN << "" << Colors::RESET << " Disabled " << name << "\n";
std::println("{}", successString("Disabled {}", name));
return ret;
}
ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
if (headersValid() != HEADERS_OK) {
std::cerr << "\n" << std::string{Colors::RED} + "" + Colors::RESET + " headers are not up-to-date, please run hyprpm update.\n";
std::println(stderr, "\n{}", failureString("headers are not up-to-date, please run hyprpm update."));
return LOADSTATE_HEADERS_OUTDATED;
}
const auto HOME = getenv("HOME");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!HOME || !HIS) {
std::cerr << "PluginManager: no $HOME or HIS\n";
std::println(stderr, "PluginManager: no $HOME or HIS");
return LOADSTATE_FAIL;
}
const auto HYPRPMPATH = DataState::getDataStatePath() + "/";
@ -770,14 +775,14 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
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
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)
pluginLines = pluginLines.substr(pluginLines.find("\n") + 1);
if (pluginLines.find('\n') != std::string::npos)
pluginLines = pluginLines.substr(pluginLines.find('\n') + 1);
else
pluginLines = "";
@ -794,8 +799,8 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
const auto REPOS = DataState::getAllRepositories();
auto enabled = [REPOS](const std::string& plugin) -> bool {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
for (auto const& r : REPOS) {
for (auto const& p : r.plugins) {
if (p.name == plugin && p.enabled)
return true;
}
@ -805,8 +810,8 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
};
auto repoForName = [REPOS](const std::string& name) -> std::string {
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
for (auto const& r : REPOS) {
for (auto const& p : r.plugins) {
if (p.name == name)
return r.name;
}
@ -816,17 +821,17 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
};
// unload disabled plugins
for (auto& p : loadedPlugins) {
for (auto const& p : loadedPlugins) {
if (!enabled(p)) {
// unload
loadUnloadPlugin(HYPRPMPATH + repoForName(p) + "/" + p + ".so", false);
std::cout << Colors::GREEN << "" << Colors::RESET << " Unloaded " << p << "\n";
std::println("{}", successString("Unloaded {}", p));
}
}
// load enabled plugins
for (auto& r : REPOS) {
for (auto& p : r.plugins) {
for (auto const& r : REPOS) {
for (auto const& p : r.plugins) {
if (!p.enabled)
continue;
@ -834,11 +839,11 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState() {
continue;
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;
}
@ -855,17 +860,18 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories();
for (auto& r : REPOS) {
std::cout << std::string{Colors::RESET} + " → Repository " + r.name + ":\n";
for (auto const& r : REPOS) {
std::println("{}", infoString("Repository {}:", r.name));
for (auto& p : r.plugins) {
std::cout << std::string{Colors::RESET} + " │ Plugin " + p.name;
for (auto const& p : r.plugins) {
std::println(" │ Plugin {}", p.name);
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
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) {
switch (err) {
case HEADERS_CORRUPTED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers corrupted. Please run hyprpm update to fix those.\n";
case HEADERS_MISMATCHED: return std::string{Colors::RED} + "" + Colors::RESET + " Headers version mismatch. Please run hyprpm update to fix those.\n";
case HEADERS_NOT_HYPRLAND: return std::string{Colors::RED} + "" + Colors::RESET + " It doesn't seem you are running on hyprland.\n";
case HEADERS_MISSING: return std::string{Colors::RED} + "" + Colors::RESET + " Headers missing. Please run hyprpm update to fix those.\n";
case HEADERS_CORRUPTED: return failureString("Headers corrupted. 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 failureString("It doesn't seem you are running on hyprland.\n");
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
case HEADERS_DUPLICATED: {
return std::string{Colors::RED} + "" + Colors::RESET + " Headers duplicated!!! This is a very bad sign.\n" +
" This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" +
" If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n";
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"
"If the above doesn't apply, check your /usr/include and /usr/local/include directories\n and remove all the hyprland headers.\n");
}
default: break;
}
return std::string{Colors::RED} + "" + Colors::RESET + " Unknown header error. Please run hyprpm update to fix those.\n";
return failureString("Unknown header error. Please run hyprpm update to fix those.\n");
}
std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
@ -904,9 +910,9 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
}
bool CPluginManager::hasDeps() {
std::vector<std::string> deps = {"meson", "cpio", "cmake"};
for (auto& d : deps) {
if (!execAndGet("which " + d + " 2>&1").contains("/"))
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config"};
for (auto const& d : deps) {
if (!execAndGet("command -v " + d).contains("/"))
return false;
}

View file

@ -2,6 +2,7 @@
#include <memory>
#include <string>
#include <utility>
enum eHeadersErrors {
HEADERS_OK = 0,
@ -68,7 +69,7 @@ class CPluginManager {
std::string headerError(const eHeadersErrors err);
std::string headerErrorShort(const eHeadersErrors err);
std::string m_szWorkingPluginDirectory = "";
std::string m_szWorkingPluginDirectory;
};
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/StringUtils.hpp"
#include "core/PluginManager.hpp"
#include "core/DataState.hpp"
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <print>
#include <chrono>
#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
is optional, when set, commit locks are ignored.
@ -30,14 +31,14 @@ const std::string HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
)#";
int main(int argc, char** argv, char** envp) {
int main(int argc, char** argv, char** envp) {
std::vector<std::string> ARGS{argc};
for (int i = 0; i < argc; ++i) {
ARGS[i] = std::string{argv[i]};
}
if (ARGS.size() < 2) {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 1;
}
@ -47,7 +48,7 @@ int main(int argc, char** argv, char** envp) {
for (int i = 1; i < argc; ++i) {
if (ARGS[i].starts_with("-")) {
if (ARGS[i] == "--help" || ARGS[i] == "-h") {
std::cout << HELP;
std::println("{}", HELP);
return 0;
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true;
@ -57,9 +58,9 @@ int main(int argc, char** argv, char** envp) {
noShallow = true;
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
force = true;
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
} else {
std::cerr << "Unrecognized option " << ARGS[i] << "\n";
std::println(stderr, "Unrecognized option {}", ARGS[i]);
return 1;
}
} else {
@ -68,7 +69,7 @@ int main(int argc, char** argv, char** envp) {
}
if (command.empty()) {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 0;
}
@ -78,7 +79,7 @@ int main(int argc, char** argv, char** envp) {
if (command[0] == "add") {
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;
}
@ -90,7 +91,7 @@ int main(int argc, char** argv, char** envp) {
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
} else if (command[0] == "remove") {
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;
}
@ -116,12 +117,12 @@ int main(int argc, char** argv, char** envp) {
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
} else if (command[0] == "enable") {
if (ARGS.size() < 2) {
std::cerr << Colors::RED << "" << Colors::RESET << " Not enough args for enable.\n";
std::println(stderr, "{}", failureString("Not enough args for enable."));
return 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;
}
@ -130,12 +131,12 @@ int main(int argc, char** argv, char** envp) {
return 1;
} else if (command[0] == "disable") {
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;
}
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;
}
@ -160,9 +161,9 @@ int main(int argc, char** argv, char** envp) {
} else if (command[0] == "list") {
g_pPluginManager->listAllPlugins();
} else {
std::cout << HELP;
std::println(stderr, "{}", HELP);
return 1;
}
return 0;
}
}

View file

@ -1,15 +1,31 @@
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n')
executable('hyprpm', src,
executable(
'hyprpm',
src,
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
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('../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')
install_data(
'../hyprpm.bash',
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 <iostream>
#include <sys/ioctl.h>
#include <algorithm>
#include <cmath>
#include <format>
#include <sys/ioctl.h>
#include <print>
#include <stdio.h>
#include <unistd.h>
@ -16,11 +16,12 @@ void CProgressBar::printMessageAbove(const std::string& msg) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r" << msg << "\n";
std::println("\r{}\r{}", spaces, msg);
print();
}
@ -29,15 +30,16 @@ void CProgressBar::print() {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (m_bFirstPrint)
std::cout << "\n";
std::print("\n");
m_bFirstPrint = false;
std::string spaces;
spaces.reserve(w.ws_col);
for (size_t i = 0; i < w.ws_col; ++i) {
spaces += ' ';
}
std::cout << "\r" << spaces << "\r";
std::print("\r{}\r", spaces);
std::string message = "";
@ -74,7 +76,7 @@ void CProgressBar::print() {
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
// draw message
std::cout << message + " " + m_szCurrentMessage;
std::print("{} {}", message, m_szCurrentMessage);
std::fflush(stdout);
}
}

View file

@ -14,4 +14,4 @@ class CProgressBar {
private:
bool m_bFirstPrint = true;
};
};

View file

@ -1,13 +1,17 @@
project('Hyprland', 'cpp', 'c',
version : run_command('cat', join_paths(meson.source_root(), 'VERSION'), check: true).stdout().strip(),
default_options : [
project(
'Hyprland',
'cpp',
'c',
version: run_command('cat', join_paths(meson.project_source_root(), 'VERSION'), check: true).stdout().strip(),
default_options: [
'warning_level=2',
'default_library=static',
'optimization=3',
'buildtype=release',
'debug=false',
'cpp_std=c++23',
])
'cpp_std=c++26',
],
)
datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"'
add_project_arguments(
@ -16,16 +20,20 @@ add_project_arguments(
'-Wno-unused-value',
'-Wno-missing-field-initializers',
'-Wno-narrowing',
'-Wno-pointer-arith',
datarootdir,
'-Wno-pointer-arith', datarootdir,
'-DHYPRLAND_VERSION="' + meson.project_version() + '"',
],
language: 'cpp')
language: 'cpp',
)
cpp_compiler = meson.get_compiler('cpp')
if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
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_composite_dep = dependency('xcb-composite', 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_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland'))
cmake = import('cmake')
udis = cmake.subproject('udis86')
udis86 = udis.dependency('libudis86')
gio_dep = dependency('gio-2.0', required: true)
if not xcb_dep.found()
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
@ -45,6 +51,7 @@ endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs
# Handle options
if get_option('systemd').enabled()
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
endif
@ -57,14 +64,22 @@ if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
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)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)
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('src')
subdir('hyprctl')
@ -73,6 +88,7 @@ subdir('assets')
subdir('example')
subdir('docs')
# Generate hyprland.pc
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
import('pkgconfig').generate(

View file

@ -1,3 +1,4 @@
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
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,
stdenv,
stdenvAdapters,
pkg-config,
pkgconf,
makeWrapper,
cmake,
meson,
ninja,
aquamarine,
binutils,
cairo,
expat,
fribidi,
git,
hwdata,
hyprcursor,
hyprland-protocols,
hyprlang,
hyprutils,
hyprwayland-scanner,
jq,
libGL,
libdatrie,
libdisplay-info,
libdrm,
libexecinfo,
libinput,
libliftoff,
libselinux,
libsepol,
libthai,
libuuid,
libxkbcommon,
libuuid,
mesa,
pango,
pciutils,
pcre2,
python3,
seatd,
systemd,
tomlplusplus,
udis86-hyprland,
wayland,
wayland-protocols,
wayland-scanner,
@ -50,134 +40,146 @@
wrapRuntimeDeps ? true,
version ? "git",
commit,
revCount,
date,
# deprecated flags
enableNvidiaPatches ? false,
nvidiaPatches ? false,
hidpiXWayland ? false,
}:
assert lib.assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
assert lib.assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert lib.assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
stdenv.mkDerivation {
pname = "hyprland${lib.optionalString debug "-debug"}";
inherit version;
}: let
inherit (builtins) baseNameOf foldl';
inherit (lib.asserts) assertMsg;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) flatten concatLists optional optionals;
inherit (lib.sources) cleanSourceWith cleanSource;
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable;
src = lib.cleanSourceWith {
filter = name: type: let
baseName = baseNameOf (toString name);
in
! (lib.hasSuffix ".nix" baseName);
src = lib.cleanSource ../.;
};
adapters = flatten [
stdenvAdapters.useMoldLinker
];
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
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;
# Remove extra @PREFIX@ to fix pkg-config paths
sed -i "s#@PREFIX@/##g" hyprland.pc.in
'';
src = cleanSourceWith {
filter = name: _type: let
baseName = baseNameOf (toString name);
in
! (hasSuffix ".nix" baseName);
src = cleanSource ../.;
};
COMMITS = commit;
DATE = date;
DIRTY = lib.optionalString (commit == "") "dirty";
HASH = commit;
postPatch = ''
# Fix hardcoded paths to /usr installation
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
nativeBuildInputs = [
hyprwayland-scanner
jq
makeWrapper
cmake
ninja
pkg-config
python3 # for udis86
wayland-scanner
];
# Remove extra @PREFIX@ to fix pkg-config paths
sed -i "s#@PREFIX@/##g" hyprland.pc.in
'';
outputs = [
"out"
"man"
"dev"
];
COMMITS = revCount;
DATE = date;
DIRTY = optionalString (commit == "") "dirty";
HASH = commit;
buildInputs = lib.concatLists [
[
aquamarine
cairo
expat
fribidi
git
hwdata
hyprcursor
hyprlang
hyprutils
libdatrie
libdisplay-info
libdrm
libGL
libinput
libliftoff
libselinux
libsepol
libthai
libuuid
libxkbcommon
mesa
pango
pciutils
pcre2
seatd
tomlplusplus
wayland
wayland-protocols
]
(lib.optionals stdenv.hostPlatform.isMusl [libexecinfo])
(lib.optionals enableXWayland [
xorg.libxcb
xorg.libXcursor
xorg.libXdmcp
xorg.xcbutil
xorg.xcbutilerrors
xorg.xcbutilrenderutil
xorg.xcbutilwm
xwayland
])
(lib.optionals withSystemd [systemd])
];
depsBuildBuild = [
pkg-config
];
cmakeBuildType =
if debug
then "Debug"
else "RelWithDebInfo";
nativeBuildInputs = [
hyprwayland-scanner
makeWrapper
meson
ninja
pkg-config
];
# we want as much debug info as possible
dontStrip = debug;
outputs = [
"out"
"man"
"dev"
];
cmakeFlags = [
(lib.cmakeBool "NO_XWAYLAND" (!enableXWayland))
(lib.cmakeBool "LEGACY_RENDERER" legacyRenderer)
(lib.cmakeBool "NO_SYSTEMD" (!withSystemd))
];
postInstall = ''
${lib.optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${lib.makeBinPath [
binutils
buildInputs = concatLists [
[
aquamarine
cairo
git
hyprcursor
hyprland-protocols
hyprlang
hyprutils
libdrm
libGL
libinput
libuuid
libxkbcommon
mesa
pango
pciutils
pkgconf
]}
''}
'';
tomlplusplus
udis86-hyprland
wayland
wayland-protocols
wayland-scanner
xorg.libXcursor
]
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
(optionals enableXWayland [
xorg.libxcb
xorg.libXdmcp
xorg.xcbutilerrors
xorg.xcbutilrenderutil
xorg.xcbutilwm
xwayland
])
(optional withSystemd systemd)
];
passthru.providedSessions = ["hyprland"];
mesonBuildType =
if debug
then "debugoptimized"
else "release";
meta = {
homepage = "https://github.com/hyprwm/Hyprland";
description = "Dynamic tiling Wayland compositor that doesn't sacrifice on its looks";
license = lib.licenses.bsd3;
platforms = lib.platforms.linux;
mainProgram = "Hyprland";
};
}
# we want as much debug info as possible
dontStrip = debug;
mesonFlags = flatten [
(mapAttrsToList mesonEnable {
"xwayland" = enableXWayland;
"legacy_renderer" = legacyRenderer;
"systemd" = withSystemd;
})
(mapAttrsToList mesonBool {
"b_pch" = false;
"tracy_enable" = false;
})
];
postInstall = ''
${optionalString wrapRuntimeDeps ''
wrapProgram $out/bin/Hyprland \
--suffix PATH : ${makeBinPath [
binutils
pciutils
pkgconf
]}
''}
'';
passthru.providedSessions = ["hyprland"];
meta = {
homepage = "https://github.com/hyprwm/Hyprland";
description = "Dynamic tiling Wayland compositor that doesn't sacrifice on its looks";
license = lib.licenses.bsd3;
platforms = lib.platforms.linux;
mainProgram = "Hyprland";
};
}

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
inputs.aquamarine.overlays.default
inputs.hyprcursor.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprlang.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default
self.overlays.udis86
# Hyprland packages themselves
(final: prev: let
(final: _prev: let
date = mkDate (self.lastModifiedDate or "19700101");
in {
hyprland = final.callPackage ./default.nix {
stdenv = final.gcc13Stdenv;
stdenv = final.gcc14Stdenv;
version = "${version}+date=${date}_${self.shortRev or "dirty"}";
commit = self.rev or "";
revCount = self.sourceInfo.revCount or "";
inherit date;
};
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
@ -62,4 +65,20 @@ in {
hyprland-extras = lib.composeManyExtensions [
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',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)
hyprland_protos = dependency('hyprland-protocols',
version: '>=0.2',
fallback: 'hyprland-protocols',
hyprland_protos = dependency(
'hyprland-protocols',
version: '>=0.4',
fallback: 'hyprland-protocols',
)
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
hl_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
wayland_protocol_dir = wayland_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_dep.get_variable('hyprwayland_scanner'),
native: true,
)
new_protocols = [
['wlr-gamma-control-unstable-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
['input-method-unstable-v2.xml'],
['virtual-keyboard-unstable-v1.xml'],
['wlr-virtual-pointer-unstable-v1.xml'],
['wlr-output-management-unstable-v1.xml'],
['kde-server-decoration.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wayland-drm.xml'],
['wlr-data-control-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/relative-pointer/relative-pointer-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'staging/alpha-modifier/alpha-modifier-v1.xml'],
[wl_protocol_dir, 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml'],
[wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
[wl_protocol_dir, 'staging/xdg-activation/xdg-activation-v1.xml'],
[wl_protocol_dir, 'staging/ext-idle-notify/ext-idle-notify-v1.xml'],
[wl_protocol_dir, 'staging/ext-session-lock/ext-session-lock-v1.xml'],
[wl_protocol_dir, 'stable/tablet/tablet-v2.xml'],
[wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'],
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'],
[wl_protocol_dir, 'staging/xwayland-shell/xwayland-shell-v1.xml'],
[wl_protocol_dir, 'stable/viewporter/viewporter.xml'],
[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'],
[wl_protocol_dir, 'staging/drm-lease/drm-lease-v1.xml'],
[wl_protocol_dir, 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml'],
protocols = [
'wlr-gamma-control-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
'input-method-unstable-v2.xml',
'virtual-keyboard-unstable-v1.xml',
'wlr-virtual-pointer-unstable-v1.xml',
'wlr-output-management-unstable-v1.xml',
'kde-server-decoration.xml',
'wlr-layer-shell-unstable-v1.xml',
'wayland-drm.xml',
'wlr-data-control-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
wayland_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wayland_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml',
wayland_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
wayland_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
wayland_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
wayland_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wayland_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml',
wayland_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml',
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml',
wayland_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wayland_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
wayland_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
wayland_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
wayland_protocol_dir / 'stable/tablet/tablet-v2.xml',
wayland_protocol_dir / 'stable/presentation-time/presentation-time.xml',
wayland_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wayland_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml',
wayland_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wayland_protocol_dir / 'stable/viewporter/viewporter.xml',
wayland_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-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_protos_headers = []
new_wl_protos = []
foreach p : new_protocols
xml = join_paths(p)
new_wl_protos += custom_target(
xml.underscorify(),
input: xml,
wl_protocols = []
foreach protocol : protocols
wl_protocols += custom_target(
protocol.underscorify(),
input: protocol,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
@ -78,31 +80,26 @@ foreach p : new_protocols
)
endforeach
wayland_server_dep = dependency('wayland-server', version: '>=1.20.0')
wayland_server_dir = wayland_server_dep.get_variable('pkgdatadir')
# wayland.xml generation
wayland_scanner = dependency('wayland-scanner')
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
wl_server_protos = [
wayland_server_dir / 'wayland.xml'
]
wl_server_protos_gen = []
foreach p : wl_server_protos
wl_server_protos_gen += custom_target(
p.underscorify(),
input: p,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
)
endforeach
wayland_xml = wayland_scanner_datadir / 'wayland.xml'
wayland_protocol = custom_target(
wayland_xml.underscorify(),
input: wayland_xml,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
)
lib_server_protos = static_library(
'server_protos',
wl_protos_src + wl_protos_headers + new_wl_protos + wl_server_protos_gen,
dependencies: wayland_server_dep.partial_dependency(compile_args: true),
wl_protocols + wayland_protocol,
)
server_protos = declare_dependency(
link_with: lib_server_protos,
sources: wl_protos_headers + new_wl_protos,
sources: wl_protocols + wayland_protocol,
)

View file

@ -1,13 +1,13 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f54cdf5d..ad7c3e73 100755
index f26a5c3c..3dfef333 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -130,6 +130,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan")
@@ -143,6 +143,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Enabling ASan")
target_link_libraries(Hyprland asan)
+ pkg_check_modules(ffidep REQUIRED IMPORTED_TARGET libffi)
+ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a PkgConfig::ffidep)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()
target_link_libraries(Hyprland asan)
+ pkg_check_modules(ffidep REQUIRED IMPORTED_TARGET libffi)
+ target_link_libraries(Hyprland ${CMAKE_SOURCE_DIR}/libwayland-server.a PkgConfig::ffidep)
target_compile_options(Hyprland PUBLIC -fsanitize=address)
endif()

File diff suppressed because it is too large Load diff

View file

@ -46,55 +46,58 @@ class CCompositor {
CCompositor();
~CCompositor();
wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop;
int m_iDRMFD = -1;
bool m_bInitialized = false;
SP<Aquamarine::CBackend> m_pAqBackend;
wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop;
int m_iDRMFD = -1;
bool m_bInitialized = false;
SP<Aquamarine::CBackend> m_pAqBackend;
std::string m_szHyprTempDataRoot = "";
std::string m_szHyprTempDataRoot = "";
std::string m_szWLDisplaySocket = "";
std::string m_szInstanceSignature = "";
std::string m_szInstancePath = "";
std::string m_szCurrentSplash = "error";
std::string m_szWLDisplaySocket = "";
std::string m_szInstanceSignature = "";
std::string m_szInstancePath = "";
std::string m_szCurrentSplash = "error";
std::vector<SP<CMonitor>> m_vMonitors;
std::vector<SP<CMonitor>> m_vRealMonitors; // for all monitors, even those turned off
std::vector<PHLWINDOW> m_vWindows;
std::vector<PHLLS> m_vLayers;
std::vector<PHLWORKSPACE> m_vWorkspaces;
std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
std::vector<PHLLSREF> m_vSurfacesFadingOut;
std::vector<SP<CMonitor>> m_vMonitors;
std::vector<SP<CMonitor>> m_vRealMonitors; // for all monitors, even those turned off
std::vector<PHLWINDOW> m_vWindows;
std::vector<PHLLS> m_vLayers;
std::vector<PHLWORKSPACE> m_vWorkspaces;
std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
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 startCompositor();
void stopCompositor();
void cleanup();
void createLockFile();
void removeLockFile();
void bumpNofile();
void restoreNofile();
void initServer(std::string socketName, int socketFd);
void startCompositor();
void stopCompositor();
void cleanup();
void createLockFile();
void removeLockFile();
void bumpNofile();
void restoreNofile();
WP<CWLSurfaceResource> m_pLastFocus;
PHLWINDOWREF m_pLastWindow;
WP<CMonitor> m_pLastMonitor;
WP<CWLSurfaceResource> m_pLastFocus;
PHLWINDOWREF m_pLastWindow;
WP<CMonitor> m_pLastMonitor;
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
bool m_bReadyToProcess = false;
bool m_bSessionActive = true;
bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bIsShuttingDown = false;
bool m_bReadyToProcess = false;
bool m_bSessionActive = true;
bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
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* getMonitorFromDesc(const std::string&);
CMonitor* getMonitorFromCursor();
@ -114,38 +117,38 @@ class CCompositor {
PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE);
bool isWorkspaceVisibleNotCovered(PHLWORKSPACE);
PHLWORKSPACE getWorkspaceByID(const int&);
PHLWORKSPACE getWorkspaceByID(const WORKSPACEID&);
PHLWORKSPACE getWorkspaceByName(const std::string&);
PHLWORKSPACE getWorkspaceByString(const std::string&);
void sanityCheckWorkspaces();
void updateWorkspaceWindowDecos(const int&);
void updateWorkspaceWindowData(const int&);
int getWindowsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroupsOnWorkspace(const int& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
void updateWorkspaceWindowDecos(const WORKSPACEID&);
void updateWorkspaceWindowData(const WORKSPACEID&);
int getWindowsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
int getGroupsOnWorkspace(const WORKSPACEID& id, std::optional<bool> onlyTiled = {}, std::optional<bool> onlyVisible = {});
PHLWINDOW getUrgentWindow();
bool hasUrgentWindowOnWorkspace(const int&);
PHLWINDOW getFirstWindowOnWorkspace(const int&);
PHLWINDOW getTopLeftWindowOnWorkspace(const int&);
PHLWINDOW getFullscreenWindowOnWorkspace(const int&);
bool hasUrgentWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFirstWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getTopLeftWindowOnWorkspace(const WORKSPACEID&);
PHLWINDOW getFullscreenWindowOnWorkspace(const WORKSPACEID&);
bool isWindowActive(PHLWINDOW);
void changeWindowZOrder(PHLWINDOW, bool);
void cleanupFadingOut(const int& monid);
void cleanupFadingOut(const MONITORID& monid);
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
PHLWINDOW getNextWindowOnWorkspace(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 isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr);
CMonitor* getMonitorInDirection(const char&);
CMonitor* getMonitorInDirection(CMonitor*, const char&);
void updateAllWindowsAnimatedDecorationValues();
void updateWorkspaceWindows(const int64_t& id);
void updateWorkspaceWindows(const WORKSPACEID& id);
void updateWindowAnimatedDecorationValues(PHLWINDOW);
int getNextAvailableMonitorID(std::string const& name);
MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, CMonitor*, bool noWarpCursor = false);
void swapActiveWorkspaces(CMonitor*, CMonitor*);
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int64_t&);
bool workspaceIDOutOfBounds(const WORKSPACEID&);
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const sFullscreenState state);
@ -162,12 +165,13 @@ class CCompositor {
PHLLS getLayerSurfaceFromSurface(SP<CWLSurfaceResource>);
void closeWindow(PHLWINDOW);
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
void forceReportSizesToWindowsOnWorkspace(const int&);
PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused!
void renameWorkspace(const int&, const std::string& name = "");
void forceReportSizesToWindowsOnWorkspace(const WORKSPACEID&);
PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, 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*);
bool isWorkspaceSpecial(const int&);
int getNewSpecialID();
bool isWorkspaceSpecial(const WORKSPACEID&);
WORKSPACEID getNewSpecialID();
void performUserChecks();
void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace);
PHLWINDOW getForceFocus();

View file

@ -52,4 +52,8 @@ struct SHyprCtlCommand {
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;

File diff suppressed because it is too large Load diff

View file

@ -3,8 +3,11 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp"
#include "config/ConfigValue.hpp"
#include "helpers/varlist/VarList.hpp"
#include "../protocols/LayerShell.hpp"
#include "../xwayland/XWayland.hpp"
#include "../protocols/OutputManagement.hpp"
#include <cstddef>
#include <cstdint>
@ -27,7 +30,9 @@
#include <filesystem>
using namespace Hyprutils::String;
extern "C" char** environ;
extern "C" char** environ;
#include "ConfigDescriptions.hpp"
static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) {
std::string V = VALUE;
@ -42,7 +47,7 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void**
std::string parseError = "";
for (auto& var : varlist) {
for (auto const& var : varlist) {
if (var.find("deg") != std::string::npos) {
// last arg
try {
@ -137,6 +142,18 @@ static Hyprlang::CParseResult handleExecOnce(const char* c, const char* v) {
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) {
const std::string VALUE = v;
const std::string COMMAND = c;
@ -311,8 +328,6 @@ CConfigManager::CConfigManager() {
configPaths.emplace_back(getMainConfigPath());
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:no_border_on_floating", Hyprlang::INT{0});
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_exception_regex", {STRVAL_EMPTY});
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:render_ahead_of_time", Hyprlang::INT{0});
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:initial_workspace_tracking", 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: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:font_family", {STRVAL_EMPTY});
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:special_scale_factor", {1.f});
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: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_resizing", Hyprlang::INT{1});
@ -438,7 +458,6 @@ CConfigManager::CConfigManager() {
m_pConfig->addConfigValue("master:always_center_master", Hyprlang::INT{0});
m_pConfig->addConfigValue("master:new_on_active", {"none"});
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:inherit_fullscreen", Hyprlang::INT{1});
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("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:special_fallthrough", Hyprlang::INT{0});
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_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: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:min_refresh_rate", Hyprlang::INT{24});
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:persistent_warps", 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_rigid", Hyprlang::INT{0});
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_touch", Hyprlang::INT{1});
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_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
m_pConfig->addSpecialCategory("device", {"name"});
@ -601,6 +625,7 @@ CConfigManager::CConfigManager() {
// keywords
m_pConfig->registerHandler(&::handleRawExec, "exec", {false});
m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false});
m_pConfig->registerHandler(&::handleExecShutdown, "exec-shutdown", {false});
m_pConfig->registerHandler(&::handleMonitor, "monitor", {false});
m_pConfig->registerHandler(&::handleBind, "bind", {true});
m_pConfig->registerHandler(&::handleUnbind, "unbind", {false});
@ -661,6 +686,10 @@ std::string CConfigManager::getMainConfigPath() {
if (!g_pCompositor->explicitConfigPath.empty())
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");
if (paths.first.has_value()) {
return paths.first.value();
@ -720,7 +749,6 @@ void CConfigManager::setDefaultAnimationVars() {
INITANIMCFG("fade");
INITANIMCFG("border");
INITANIMCFG("borderangle");
INITANIMCFG("workspaces");
// windows
INITANIMCFG("windowsIn");
@ -741,7 +769,12 @@ void CConfigManager::setDefaultAnimationVars() {
// border
// workspaces
INITANIMCFG("workspaces");
INITANIMCFG("workspacesIn");
INITANIMCFG("workspacesOut");
INITANIMCFG("specialWorkspace");
INITANIMCFG("specialWorkspaceIn");
INITANIMCFG("specialWorkspaceOut");
}
// init the values
@ -770,7 +803,11 @@ void CConfigManager::setDefaultAnimationVars() {
CREATEANIMCFG("fadeLayersIn", "fadeLayers");
CREATEANIMCFG("fadeLayersOut", "fadeLayers");
CREATEANIMCFG("workspacesIn", "workspaces");
CREATEANIMCFG("workspacesOut", "workspaces");
CREATEANIMCFG("specialWorkspace", "workspaces");
CREATEANIMCFG("specialWorkspaceIn", "specialWorkspace");
CREATEANIMCFG("specialWorkspaceOut", "specialWorkspace");
}
std::optional<std::string> CConfigManager::resetHLConfig() {
@ -785,6 +822,7 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
m_vDeclaredPlugins.clear();
m_dLayerRules.clear();
m_vFailedPluginConfigValues.clear();
finalExecRequests.clear();
// paths
configPaths.clear();
@ -798,11 +836,14 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
}
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();
}
for (auto& m : g_pCompositor->m_vMonitors)
for (auto const& m : g_pCompositor->m_vMonitors)
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
// 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")))
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)
g_pHyprError->queueCreate("Warning: You're using an autogenerated config! (config file: " + getMainConfigPath() + " )\nSUPER+Q -> kitty\nSUPER+M -> exit Hyprland",
CColor(1.0, 1.0, 70.0 / 255.0, 1.0));
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));
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
g_pHyprError->destroy();
@ -842,11 +887,34 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
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)
refreshGroupBarGradients();
// Updates dynamic window and workspace rules
for (auto& w : g_pCompositor->m_vWorkspaces) {
for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->inert())
continue;
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());
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
// mark blur dirty
g_pHyprOpenGL->markBlurDirtyForMonitor(m.get());
@ -884,7 +952,7 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
m->forceFullFrames = 2;
// also force mirrors, as the aspect ratio could've changed
for (auto& mirror : m->mirrors)
for (auto const& mirror : m->mirrors)
mirror->forceFullFrames = 3;
}
@ -916,14 +984,24 @@ void CConfigManager::init() {
}
std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) {
const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str());
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());
// invalidate layouts if they changed
if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) {
for (auto& m : g_pCompositor->m_vMonitors)
for (auto const& m : g_pCompositor->m_vMonitors)
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
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@ -951,7 +1029,7 @@ void CConfigManager::tick() {
bool parse = false;
for (auto& cf : configPaths) {
for (auto const& cf : configPaths) {
struct stat fileStat;
int err = stat(cf.c_str(), &fileStat);
if (err != 0) {
@ -1006,33 +1084,74 @@ std::string CConfigManager::getDeviceString(const std::string& dev, const std::s
return VAL;
}
SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) {
for (auto& r : m_dMonitorRules | std::views::reverse) {
if (PMONITOR.matchesStaticSelector(r.name)) {
return r;
SMonitorRule CConfigManager::getMonitorRuleFor(const SP<CMonitor> PMONITOR) {
auto applyWlrOutputConfig = [PMONITOR](SMonitorRule rule) -> SMonitorRule {
const auto CONFIG = PROTO::outputManagement->getOutputStateFor(PMONITOR);
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()) {
return r;
return applyWlrOutputConfig(r);
}
}
Debug::log(WARN, "No rules configured. Using the default hardcoded one.");
return SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT,
.name = "",
.resolution = Vector2D(0, 0),
.offset = Vector2D(-INT32_MAX, -INT32_MAX),
.scale = -1}; // 0, 0 is preferred and -1, -1 is auto
return applyWlrOutputConfig(SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT,
.name = "",
.resolution = Vector2D(0, 0),
.offset = Vector2D(-INT32_MAX, -INT32_MAX),
.scale = -1}); // 0, 0 is preferred and -1, -1 is auto
}
SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) {
SWorkspaceRule mergedRule{};
for (auto& rule : m_dWorkspaceRules) {
for (auto const& rule : m_dWorkspaceRules) {
if (!pWorkspace->matchesStaticSelector(rule.workspaceString))
continue;
@ -1105,7 +1224,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
// local tags for dynamic tag rule match
auto tags = pWindow->m_tags;
for (auto& rule : m_dWindowRules) {
for (auto const& rule : m_dWindowRules) {
// check if we have a matching rule
if (!rule.v2) {
try {
@ -1185,6 +1304,32 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
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()) {
const auto PWORKSPACE = pWindow->m_pWorkspace;
if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule.szOnWorkspace))
@ -1244,7 +1389,7 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo
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; })) {
returns.push_back({er.szRule, "execRule"});
anyExecFound = true;
@ -1264,7 +1409,7 @@ std::vector<SLayerRule> CConfigManager::getMatchingRules(PHLLS pLS) {
if (!pLS->layerSurface || pLS->fadingOut)
return returns;
for (auto& lr : m_dLayerRules) {
for (auto const& lr : m_dLayerRules) {
if (lr.targetNamespace.starts_with("address:0x")) {
if (std::format("address:0x{:x}", (uintptr_t)pLS.get()) != lr.targetNamespace)
continue;
@ -1301,7 +1446,7 @@ void CConfigManager::dispatchExecOnce() {
firstExecDispatched = true;
isLaunchingExecOnce = true;
for (auto& c : firstExecRequests) {
for (auto const& c : firstExecRequests) {
handleRawExec("", c);
}
@ -1318,6 +1463,24 @@ void CConfigManager::dispatchExecOnce() {
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) {
m_dMonitorRules.emplace_back(r);
}
@ -1338,11 +1501,11 @@ void CConfigManager::performMonitorReload() {
bool overAgain = false;
for (auto& m : g_pCompositor->m_vRealMonitors) {
for (auto const& m : g_pCompositor->m_vRealMonitors) {
if (!m->output || m->isUnsafeFallback)
continue;
auto rule = getMonitorRuleFor(*m);
auto rule = getMonitorRuleFor(m);
if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) {
overAgain = true;
@ -1385,7 +1548,7 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) {
}
bool CConfigManager::shouldBlurLS(const std::string& ns) {
for (auto& bls : m_dBlurLSNamespaces) {
for (auto const& bls : m_dBlurLSNamespaces) {
if (bls == ns) {
return true;
}
@ -1395,11 +1558,11 @@ bool CConfigManager::shouldBlurLS(const std::string& ns) {
}
void CConfigManager::ensureMonitorStatus() {
for (auto& rm : g_pCompositor->m_vRealMonitors) {
for (auto const& rm : g_pCompositor->m_vRealMonitors) {
if (!rm->output || rm->isUnsafeFallback)
continue;
auto rule = getMonitorRuleFor(*rm);
auto rule = getMonitorRuleFor(rm);
if (rule.disabled == rm->m_bEnabled)
g_pHyprRenderer->applyMonitorRule(rm.get(), &rule);
@ -1417,7 +1580,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
if (USEVRR == 0) {
if (m->vrrActive) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false);
if (!m->state.commit())
@ -1427,6 +1590,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
return;
} else if (USEVRR == 1) {
if (!m->vrrActive) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(true);
if (!m->state.test()) {
@ -1451,6 +1615,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
const auto WORKSPACEFULL = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN);
if (WORKSPACEFULL) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(true);
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);
} else if (!WORKSPACEFULL) {
m->output->state->resetExplicitFences();
m->output->state->setAdaptiveSync(false);
if (!m->state.commit())
@ -1475,7 +1641,7 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
return;
}
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
ensureVRRForDisplay(m.get());
}
}
@ -1497,7 +1663,7 @@ CMonitor* CConfigManager::getBoundMonitorForWS(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;
if (WSNAME == wsname)
@ -1568,7 +1734,7 @@ void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, Hy
}
void CConfigManager::removePluginConfig(HANDLE handle) {
for (auto& k : pluginKeywords) {
for (auto const& k : pluginKeywords) {
if (k.handle != handle)
continue;
@ -1576,7 +1742,7 @@ void CConfigManager::removePluginConfig(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)
continue;
@ -1591,7 +1757,7 @@ std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) {
if (other->monitor == name)
return other->workspaceString;
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)
return other->workspaceString;
}
@ -1617,6 +1783,16 @@ std::optional<std::string> CConfigManager::handleExecOnce(const std::string& com
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) {
auto args = CVarList(modeline, 0, 's');
@ -1664,7 +1840,7 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) {
if (it != flagsmap.end())
mode.flags |= it->second;
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);
@ -1988,7 +2164,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool dontInhibit = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) {
for (auto const& arg : BINDARGS) {
if (arg == 'l') {
locked = true;
} else if (arg == 'r') {
@ -2099,7 +2275,7 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
bool windowRuleValid(const std::string& RULE) {
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>{
"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) {
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); });
}
@ -2166,9 +2342,9 @@ std::optional<std::string> CConfigManager::handleLayerRule(const std::string& co
m_dLayerRules.push_back({VALUE, RULE});
for (auto& m : g_pCompositor->m_vMonitors)
for (auto& lsl : m->m_aLayerSurfaceLayers)
for (auto& ls : lsl)
for (auto const& m : g_pCompositor->m_vMonitors)
for (auto const& lsl : m->m_aLayerSurfaceLayers)
for (auto const& ls : lsl)
ls->applyRules();
return {};
@ -2189,17 +2365,18 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
rule.szRule = RULE;
rule.szValue = VALUE;
const auto TAGPOS = VALUE.find("tag:");
const auto TITLEPOS = VALUE.find("title:");
const auto CLASSPOS = VALUE.find("class:");
const auto INITIALTITLEPOS = VALUE.find("initialTitle:");
const auto INITIALCLASSPOS = VALUE.find("initialClass:");
const auto X11POS = VALUE.find("xwayland:");
const auto FLOATPOS = VALUE.find("floating:");
const auto FULLSCREENPOS = VALUE.find("fullscreen:");
const auto PINNEDPOS = VALUE.find("pinned:");
const auto FOCUSPOS = VALUE.find("focus:");
const auto ONWORKSPACEPOS = VALUE.find("onworkspace:");
const auto TAGPOS = VALUE.find("tag:");
const auto TITLEPOS = VALUE.find("title:");
const auto CLASSPOS = VALUE.find("class:");
const auto INITIALTITLEPOS = VALUE.find("initialTitle:");
const auto INITIALCLASSPOS = VALUE.find("initialClass:");
const auto X11POS = VALUE.find("xwayland:");
const auto FLOATPOS = VALUE.find("floating:");
const auto FULLSCREENPOS = VALUE.find("fullscreen:");
const auto PINNEDPOS = VALUE.find("pinned:");
const auto FOCUSPOS = VALUE.find("focus:");
const auto FULLSCREENSTATEPOS = VALUE.find("fullscreenstate:");
const auto ONWORKSPACEPOS = VALUE.find("onworkspace:");
// find workspacepos that isn't onworkspacepos
size_t WORKSPACEPOS = std::string::npos;
@ -2212,9 +2389,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
currentPos = VALUE.find("workspace:", currentPos + 1);
}
const auto checkPos = std::unordered_set{
TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, FULLSCREENPOS, PINNEDPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS,
};
const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS,
FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS};
if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) {
Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE);
return "Invalid rulev2 syntax: " + VALUE;
@ -2243,6 +2419,8 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
min = FULLSCREENPOS;
if (PINNEDPOS > pos && PINNEDPOS < min)
min = PINNEDPOS;
if (FULLSCREENSTATEPOS > pos && FULLSCREENSTATEPOS < min)
min = FULLSCREENSTATEPOS;
if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min)
min = ONWORKSPACEPOS;
if (WORKSPACEPOS > pos && WORKSPACEPOS < min)
@ -2287,6 +2465,9 @@ std::optional<std::string> CConfigManager::handleWindowRuleV2(const std::string&
if (PINNEDPOS != std::string::npos)
rule.bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0;
if (FULLSCREENSTATEPOS != std::string::npos)
rule.szFullscreenState = extract(FULLSCREENSTATEPOS + 16);
if (WORKSPACEPOS != std::string::npos)
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)
return false;
if (!rule.szFullscreenState.empty() && rule.szFullscreenState != other.szFullscreenState)
return false;
if (!rule.szWorkspace.empty() && rule.szWorkspace != other.szWorkspace)
return false;
@ -2359,9 +2543,9 @@ void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBl
matchName = matchName.substr(8);
}
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
for (auto& ls : lsl) {
for (auto const& m : g_pCompositor->m_vMonitors) {
for (auto const& lsl : m->m_aLayerSurfaceLayers) {
for (auto const& ls : lsl) {
if (BYADDRESS) {
if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName)
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 int ruleOnCreatedEmptyLen = ruleOnCreatedEmpty.length();
const static auto ruleOnCreatedEmptyLen = ruleOnCreatedEmpty.length();
auto assignRule = [&](std::string rule) -> std::optional<std::string> {
size_t delim = std::string::npos;
@ -2470,7 +2654,7 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
};
CVarList rulesList{rules, 0, ',', true};
for (auto& r : rulesList) {
for (auto const& r : rulesList) {
const auto R = assignRule(r);
if (R.has_value())
return R;
@ -2586,3 +2770,60 @@ std::optional<std::string> CConfigManager::handlePlugin(const std::string& comma
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 <unordered_map>
#include "../defines.hpp"
#include <variant>
#include <vector>
#include <deque>
#include <algorithm>
@ -33,7 +34,7 @@ struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
std::string workspaceName = "";
int workspaceId = -1;
WORKSPACEID workspaceId = -1;
bool isDefault = false;
bool isPersistent = false;
std::optional<CCssGapData> gapsIn;
@ -83,6 +84,70 @@ struct SExecRequestedRule {
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 {
public:
CConfigManager();
@ -104,7 +169,7 @@ class CConfigManager {
static std::string getMainConfigPath();
const std::string getConfigString();
SMonitorRule getMonitorRuleFor(const CMonitor&);
SMonitorRule getMonitorRuleFor(const SP<CMonitor>);
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
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<SLayerRule> getMatchingRules(PHLLS);
const std::vector<SConfigOptionDescription>& getAllDescriptions();
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
std::unordered_map<std::string, SAnimationPropertyConfig> getAnimationConfig();
@ -125,6 +192,7 @@ class CConfigManager {
// no-op when done.
void dispatchExecOnce();
void dispatchExecShutdown();
void performMonitorReload();
void appendMonitorRule(const SMonitorRule&);
@ -146,6 +214,7 @@ class CConfigManager {
// keywords
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> handleExecShutdown(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> handleUnbind(const std::string&, const std::string&);
@ -222,6 +291,7 @@ class CConfigManager {
bool firstExecDispatched = false;
bool m_bManualCrashInitiated = false;
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::string m_szConfigErrors = "";

View file

@ -72,7 +72,7 @@ env = HYPRCURSOR_SIZE,24
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
general {
gaps_in = 5
gaps_out = 20
@ -83,7 +83,7 @@ general {
col.inactive_border = rgba(595959aa)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
resize_on_border = false
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
@ -109,7 +109,7 @@ decoration {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
}
@ -130,6 +130,13 @@ animations {
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
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
@ -142,7 +149,7 @@ master {
}
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}
@ -182,9 +189,9 @@ device {
}
####################
### KEYBINDINGSS ###
####################
###################
### KEYBINDINGS ###
###################
# See https://wiki.hyprland.org/Configuring/Keywords/
$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: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 ###
@ -255,5 +275,9 @@ bindm = $mainMod, mouse:273, resizewindow
# Example windowrule v2
# 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();
}
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) {
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) {
std::string result;
for (auto& m : pMonitor->output->modes) {
for (auto const& m : pMonitor->output->modes) {
if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
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 result;
if (!m->output || m->ID == -1ull)
if (!m->output || m->ID == -1)
return "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
@ -103,6 +103,7 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"focused": {},
"dpmsStatus": {},
"vrr": {},
"solitary": "{:x}",
"activelyTearing": {},
"disabled": {},
"currentFormat": "{}",
@ -114,19 +115,20 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
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,
(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->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
(m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (uint64_t)m->solitaryClient.get(),
(m->tearingState.activelyTearing ? "true" : "false"), (m->m_bEnabled ? "false" : "true"), formatToString(m->output->state->state().drmFormat),
availableModesForOutput(m.get(), format));
} 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"
"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->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, m->output->state->state().adaptiveSync, m->tearingState.activelyTearing,
!m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
(m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, (uint64_t)m->solitaryClient.get(),
m->tearingState.activelyTearing, !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
}
return result;
@ -146,7 +148,7 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
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);
}
@ -154,20 +156,11 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "]";
} else {
for (auto& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output || m->ID == -1ull)
for (auto const& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) {
if (!m->output || m->ID == -1)
continue;
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"
"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));
result += CHyprCtl::getMonitorData(m, format);
}
}
@ -272,7 +265,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
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)
continue;
@ -283,7 +276,7 @@ std::string clientsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "]";
} 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)
continue;
@ -320,45 +313,48 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputFormat format) {
const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; };
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor));
const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : "";
const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : "";
const std::string gapsIn = (bool)(r.gapsIn) ?
std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().top, r.gapsIn.value().right, r.gapsIn.value().bottom, r.gapsIn.value().left) :
const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor));
const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : "";
const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : "";
const std::string gapsIn = (bool)(r.gapsIn) ?
std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().top, r.gapsIn.value().right, r.gapsIn.value().bottom, r.gapsIn.value().left) :
"";
const std::string gapsOut = (bool)(r.gapsOut) ?
std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().top, r.gapsOut.value().right, r.gapsOut.value().bottom, r.gapsOut.value().left) :
"";
const std::string gapsOut = (bool)(r.gapsOut) ?
std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().top, r.gapsOut.value().right, r.gapsOut.value().bottom, r.gapsOut.value().left) :
"";
const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : "";
const std::string border = (bool)(r.noBorder) ? std::format(",\n \"border\": {}", boolToString(!r.noBorder.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 shadow = (bool)(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : "";
const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : "";
const std::string border = (bool)(r.noBorder) ? std::format(",\n \"border\": {}", boolToString(!r.noBorder.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 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"#({{
"workspaceString": "{}"{}{}{}{}{}{}{}{}
std::string result =
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;
} else {
const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "<unset>" : escapeJSONStrings(r.monitor));
const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : "<unset>");
const std::string gapsIn = (bool)(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().top), std::to_string(r.gapsIn.value().right),
std::to_string(r.gapsIn.value().bottom), std::to_string(r.gapsIn.value().left)) :
std::format("\tgapsIn: <unset>\n");
const std::string gapsOut = (bool)(r.gapsOut) ? std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().top), std::to_string(r.gapsOut.value().right),
std::to_string(r.gapsOut.value().bottom), std::to_string(r.gapsOut.value().left)) :
std::format("\tgapsOut: <unset>\n");
const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : "<unset>");
const std::string border = std::format("\tborder: {}\n", (bool)(r.noBorder) ? boolToString(!r.noBorder.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 shadow = std::format("\tshadow: {}\n", (bool)(r.noShadow) ? boolToString(!r.noShadow.value()) : "<unset>");
const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "<unset>" : escapeJSONStrings(r.monitor));
const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : "<unset>");
const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : "<unset>");
const std::string gapsIn = (bool)(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().top), std::to_string(r.gapsIn.value().right),
std::to_string(r.gapsIn.value().bottom), std::to_string(r.gapsIn.value().left)) :
std::format("\tgapsIn: <unset>\n");
const std::string gapsOut = (bool)(r.gapsOut) ? std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().top), std::to_string(r.gapsOut.value().right),
std::to_string(r.gapsOut.value().bottom), std::to_string(r.gapsOut.value().left)) :
std::format("\tgapsOut: <unset>\n");
const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : "<unset>");
const std::string border = std::format("\tborder: {}\n", (bool)(r.noBorder) ? boolToString(!r.noBorder.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 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,
borderSize, border, rounding, decorate, shadow);
std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut,
borderSize, border, rounding, decorate, shadow, defaultName);
return result;
}
@ -381,7 +377,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
for (auto const& w : g_pCompositor->m_vWorkspaces) {
result += CHyprCtl::getWorkspaceData(w, format);
result += ",";
}
@ -389,7 +385,7 @@ std::string workspacesRequest(eHyprCtlOutputFormat format, std::string request)
trimTrailingComma(result);
result += "]";
} else {
for (auto& w : g_pCompositor->m_vWorkspaces) {
for (auto const& w : g_pCompositor->m_vWorkspaces) {
result += CHyprCtl::getWorkspaceData(w, format);
}
}
@ -401,7 +397,7 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
result += ",";
}
@ -409,7 +405,7 @@ std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::string reque
trimTrailingComma(result);
result += "]";
} else {
for (auto& r : g_pConfigManager->getAllWorkspaceRules()) {
for (auto const& r : g_pConfigManager->getAllWorkspaceRules()) {
result += getWorkspaceRuleData(r, format);
}
}
@ -437,7 +433,7 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "{\n";
for (auto& mon : g_pCompositor->m_vMonitors) {
for (auto const& mon : g_pCompositor->m_vMonitors) {
result += std::format(
R"#("{}": {{
"levels": {{
@ -445,13 +441,13 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
escapeJSONStrings(mon->szName));
int layerLevel = 0;
for (auto& level : mon->m_aLayerSurfaceLayers) {
for (auto const& level : mon->m_aLayerSurfaceLayers) {
result += std::format(
R"#(
"{}": [
)#",
layerLevel);
for (auto& layer : level) {
for (auto const& layer : level) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@ -484,14 +480,14 @@ std::string layersRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n}\n";
} else {
for (auto& mon : g_pCompositor->m_vMonitors) {
for (auto const& mon : g_pCompositor->m_vMonitors) {
result += std::format("Monitor {}:\n", mon->szName);
int layerLevel = 0;
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]);
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,
layer->geometry.height, layer->szNamespace);
}
@ -510,7 +506,7 @@ std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += "[";
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format(
R"#(
"{}",)#",
@ -520,7 +516,7 @@ std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n]\n";
} else {
for (auto& m : g_pLayoutManager->getAllLayoutNames()) {
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
result += std::format("{}\n", m);
}
}
@ -557,7 +553,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "{\n";
result += "\"mice\": [\n";
for (auto& m : g_pInputManager->m_vPointers) {
for (auto const& m : g_pInputManager->m_vPointers) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@ -572,7 +568,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n],\n";
result += "\"keyboards\": [\n";
for (auto& k : g_pInputManager->m_vKeyboards) {
for (auto const& k : g_pInputManager->m_vKeyboards) {
const auto KM = k->getActiveLayout();
result += std::format(
R"#( {{
@ -596,7 +592,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"tablets\": [\n";
for (auto& d : g_pInputManager->m_vTabletPads) {
for (auto const& d : g_pInputManager->m_vTabletPads) {
result += std::format(
R"#( {{
"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 : ""));
}
for (auto& d : g_pInputManager->m_vTablets) {
for (auto const& d : g_pInputManager->m_vTablets) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@ -618,7 +614,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
(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(
R"#( {{
"address": "0x{:x}",
@ -632,7 +628,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"touch\": [\n";
for (auto& d : g_pInputManager->m_vTouches) {
for (auto const& d : g_pInputManager->m_vTouches) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@ -646,7 +642,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\"switches\": [\n";
for (auto& d : g_pInputManager->m_lSwitches) {
for (auto const& d : g_pInputManager->m_lSwitches) {
result += std::format(
R"#( {{
"address": "0x{:x}",
@ -663,14 +659,14 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
} else {
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,
(m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f));
}
result += "\n\nKeyboards:\n";
for (auto& k : g_pInputManager->m_vKeyboards) {
for (auto const& k : g_pInputManager->m_vKeyboards) {
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",
(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";
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 : "");
}
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);
}
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 += "\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 += "\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() : "");
}
}
@ -712,21 +708,21 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
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,
ac.second.internalBezier, ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle);
}
ret += "beziers:\n";
for (auto& bz : g_pAnimationManager->getAllBeziers()) {
for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format("\n\tname: {}\n", bz.first);
}
} else {
// json
ret += "[[";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
for (auto const& ac : g_pConfigManager->getAnimationConfig()) {
ret += std::format(R"#(
{{
"name": "{}",
@ -744,7 +740,7 @@ std::string animationsRequest(eHyprCtlOutputFormat format, std::string request)
ret += ",\n[";
for (auto& bz : g_pAnimationManager->getAllBeziers()) {
for (auto const& bz : g_pAnimationManager->getAllBeziers()) {
ret += std::format(R"#(
{{
"name": "{}"
@ -778,11 +774,11 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
std::string ret = "";
const auto SHORTCUTS = PROTO::globalShortcuts->getAllShortcuts();
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
for (auto const& sh : SHORTCUTS)
ret += std::format("{}:{} -> {}\n", sh.appid, sh.id, sh.description);
} else {
ret += "[";
for (auto& sh : SHORTCUTS) {
for (auto const& sh : SHORTCUTS) {
ret += std::format(R"#(
{{
"name": "{}",
@ -800,7 +796,7 @@ std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::string requ
std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
std::string ret = "";
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& kb : g_pKeybindManager->m_lKeybinds) {
for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
ret += "bind";
if (kb.locked)
ret += "l";
@ -821,7 +817,7 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
} else {
// json
ret += "[";
for (auto& kb : g_pKeybindManager->m_lKeybinds) {
for (auto const& kb : g_pKeybindManager->m_lKeybinds) {
ret += std::format(
R"#(
{{
@ -857,37 +853,47 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
std::replace(commitMsg.begin(), commitMsg.end(), '#', ' ');
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" + commitMsg +
").\nDate: " + GIT_COMMIT_DATE + "\nTag: " + GIT_TAG + ", commits: " + GIT_COMMITS + "\n\nflags: (if any)\n";
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\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
result += "legacyrenderer\n";
#endif
#ifndef ISDEBUG
#ifdef ISDEBUG
result += "debug\n";
#endif
#ifdef NO_XWAYLAND
result += "no xwayland\n";
#endif
#endif
return result;
} else {
std::string result = std::format(
R"#({{
"branch": "{}",
"commit": "{}",
"version": "{}",
"dirty": {},
"commit_message": "{}",
"commit_date": "{}",
"tag": "{}",
"commits": "{}",
"buildAquamarine": "{}",
"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
result += "\"legacyrenderer\",";
#endif
#ifndef ISDEBUG
#ifdef ISDEBUG
result += "\"debug\",";
#endif
#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 += "plugins:\n";
for (auto& pl : g_pPluginSystem->getAllPlugins()) {
result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version);
}
if (g_pPluginSystem) {
for (auto const& pl : g_pPluginSystem->getAllPlugins()) {
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 += g_pConfigManager->getConfigString();
result += "\n======Config-End========\n";
@ -962,20 +971,34 @@ std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) {
if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end())
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) {
// get rid of the keyword keyword
in = in.substr(in.find_first_of(' ') + 1);
// Find the first space to strip the keyword keyword
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);
@ -1006,7 +1029,7 @@ std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) {
// decorations will probably need a repaint
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_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
}
@ -1122,46 +1145,77 @@ std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request)
}
std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' ');
CVarList vars(request, 0, ' ');
const auto KB = vars[1];
const auto CMD = vars[2];
const auto KB = vars[1];
const auto CMD = vars[2];
// get kb
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); });
SP<IKeyboard> pKeyboard;
if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found";
auto updateKeyboard = [](const SP<IKeyboard> KEEB, const std::string& CMD) -> std::optional<std::string> {
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
if (xkb_state_layout_index_is_active(KEEB->xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
break;
const auto KEEB = *PKEYBOARD;
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap);
xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) {
if (xkb_state_layout_index_is_active(KEEB->xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1)
break;
activeLayout++;
}
if (CMD == "next")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
else if (CMD == "prev")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
else {
int requestedLayout = 0;
try {
requestedLayout = std::stoi(CMD);
} catch (std::exception& e) { return "invalid arg 2"; }
if (requestedLayout < 0 || (uint64_t)requestedLayout > LAYOUTS - 1) {
return "layout idx out of range of " + std::to_string(LAYOUTS);
activeLayout++;
}
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, requestedLayout);
if (CMD == "next")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
else if (CMD == "prev")
KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
else {
int requestedLayout = 0;
try {
requestedLayout = std::stoi(CMD);
} catch (std::exception& e) { return "invalid arg 2"; }
if (requestedLayout < 0 || (uint64_t)requestedLayout > LAYOUTS - 1) {
return "layout idx out of range of " + std::to_string(LAYOUTS);
}
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";
}
@ -1286,7 +1340,7 @@ std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
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);
return "ok";
@ -1359,7 +1413,7 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
std::string result = "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
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},";
}
@ -1367,7 +1421,7 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
result += "]";
} else {
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";
}
}
@ -1386,7 +1440,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
bool added = false;
if (!vars[3].empty()) {
for (auto& m : g_pCompositor->m_vRealMonitors) {
for (auto const& m : g_pCompositor->m_vRealMonitors) {
if (m->szName == vars[3])
return "Name already taken";
}
@ -1396,7 +1450,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
if (g_pCompositor->getMonitorFromName(vars[3]))
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();
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";
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);
}
@ -1561,6 +1615,29 @@ std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) {
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() {
registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest});
registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest});
@ -1581,6 +1658,8 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
@ -1657,7 +1736,7 @@ std::string CHyprCtl::getReply(std::string request) {
std::string result = "";
// parse exact cmds first, then non-exact.
for (auto& cmd : m_vCommands) {
for (auto const& cmd : m_vCommands) {
if (!cmd->exact)
continue;
@ -1668,7 +1747,7 @@ std::string CHyprCtl::getReply(std::string request) {
}
if (result.empty())
for (auto& cmd : m_vCommands) {
for (auto const& cmd : m_vCommands) {
if (cmd->exact)
continue;
@ -1699,7 +1778,7 @@ std::string CHyprCtl::getReply(std::string request) {
rd.blurFBDirty = true;
}
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
g_pHyprRenderer->damageMonitor(m.get());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID);
}

View file

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

View file

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

View file

@ -13,8 +13,8 @@ class CHyprMonitorDebugOverlay {
public:
int draw(int offset);
void renderData(CMonitor* pMonitor, float µs);
void renderDataNoOverlay(CMonitor* pMonitor, float µs);
void renderData(CMonitor* pMonitor, float durationUs);
void renderDataNoOverlay(CMonitor* pMonitor, float durationUs);
void frameData(CMonitor* pMonitor);
private:
@ -33,8 +33,8 @@ class CHyprDebugOverlay {
public:
CHyprDebugOverlay();
void draw();
void renderData(CMonitor*, float µs);
void renderDataNoOverlay(CMonitor*, float µs);
void renderData(CMonitor*, float durationUs);
void renderDataNoOverlay(CMonitor*, float durationUs);
void frameData(CMonitor*);
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) {
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->started.reset();
PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon;
PNOTIF->fontSize = fontSize;
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
g_pCompositor->scheduleFrameForMonitor(m.get());
}
}
@ -87,7 +87,7 @@ CBox CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
const auto iconBackendID = iconBackendFromLayout(layout);
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 FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);

View file

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

View file

@ -55,8 +55,9 @@ namespace Debug {
// print date and time to the ofs
if (disableTime && !**disableTime) {
#ifndef _LIBCPP_VERSION
const auto zt = std::chrono::zoned_time{std::chrono::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())};
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())};
#else
// TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready
const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())};
@ -72,6 +73,5 @@ namespace Debug {
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
log(level, logMsg);
logMutex.unlock();
}
};

View file

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

View file

@ -71,6 +71,12 @@ CLayerSurface::~CLayerSurface() {
surface->unassign();
g_pHyprRenderer->makeEGLCurrent();
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() {
@ -360,7 +366,7 @@ void CLayerSurface::applyRules() {
xray = -1;
animationStyle.reset();
for (auto& rule : g_pConfigManager->getMatchingRules(self.lock())) {
for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) {
if (rule.rule == "noanim")
noAnimations = true;
else if (rule.rule == "blur")
@ -388,6 +394,11 @@ void CLayerSurface::applyRules() {
} else if (rule.rule.starts_with("animation")) {
CVarList vars{rule.rule, 2, 's'};
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;
uint32_t layer = 0;
int monitorID = -1;
MONITORID monitorID = -1;
bool fadingOut = false;
bool readyToDelete = false;
@ -51,10 +51,11 @@ class CLayerSurface {
bool forceBlur = false;
bool forceBlurPopups = false;
int xray = -1;
int64_t xray = -1;
bool ignoreAlpha = false;
float ignoreAlphaValue = 0.f;
bool dimAround = false;
int64_t order = 0;
std::optional<std::string> animationStyle;

View file

@ -202,6 +202,13 @@ void CPopup::reposition() {
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 offset;
@ -251,7 +258,7 @@ void CPopup::recheckTree() {
void CPopup::recheckChildrenRecursive() {
auto cpy = m_vChildren;
for (auto& c : cpy) {
for (auto const& c : cpy) {
c->onCommit(true);
c->recheckChildrenRecursive();
}
@ -282,14 +289,14 @@ bool CPopup::visible() {
}
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);
}
std::vector<CPopup*> nodes2;
for (auto& n : nodes) {
for (auto& c : n->m_vChildren) {
for (auto const& n : nodes) {
for (auto const& c : n->m_vChildren) {
nodes2.push_back(c.get());
}
}
@ -308,7 +315,7 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
std::vector<CPopup*> 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)
continue;

View file

@ -18,21 +18,22 @@ class CPopup {
~CPopup();
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
SP<CWLSurface> getT1Owner();
Vector2D coordsRelativeToParent();
Vector2D coordsGlobal();
Vector2D size();
Vector2D size();
void onNewPopup(SP<CXDGPopupResource> popup);
void onDestroy();
void onMap();
void onUnmap();
void onCommit(bool ignoreSiblings = false);
void onReposition();
void onNewPopup(SP<CXDGPopupResource> popup);
void onDestroy();
void onMap();
void onUnmap();
void onCommit(bool ignoreSiblings = false);
void onReposition();
void recheckTree();
void recheckTree();
bool visible();
bool visible();
// will also loop over this node
void breadthfirst(std::function<void(CPopup*, void*)> fn, void* data);

View file

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

View file

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

View file

@ -62,7 +62,7 @@ bool CWLSurface::small() const {
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 {

View file

@ -19,9 +19,8 @@ using namespace Hyprutils::String;
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
pWindow->m_pSelf = pWindow;
pWindow->m_bIsX11 = true;
pWindow->m_iX11Type = surface->overrideRedirect ? 2 : 1;
pWindow->m_pSelf = pWindow;
pWindow->m_bIsX11 = true;
pWindow->m_vRealPosition.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_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
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<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_cRealShadowColor.create(g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW);
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<CHyprBorderDecoration>(pWindow));
@ -252,7 +253,7 @@ void CWindow::updateWindowDecos() {
if (!m_bIsMapped || isHidden())
return;
for (auto& wd : m_vDecosToRemove) {
for (auto const& wd : m_vDecosToRemove) {
for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) {
if (it->get() == wd) {
g_pDecorationPositioner->uncacheDecoration(it->get());
@ -270,11 +271,11 @@ void CWindow::updateWindowDecos() {
// make a copy because updateWindow can remove decos.
std::vector<IHyprWindowDecoration*> decos;
for (auto& wd : m_dWindowDecorations) {
for (auto const& wd : m_dWindowDecorations) {
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())
continue;
wd->updateWindow(m_pSelf.lock());
@ -296,7 +297,7 @@ void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
}
void CWindow::uncacheWindowDecos() {
for (auto& wd : m_dWindowDecorations) {
for (auto const& wd : m_dWindowDecorations) {
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))
return false;
for (auto& wd : m_dWindowDecorations) {
for (auto const& wd : m_dWindowDecorations) {
if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
continue;
@ -337,7 +338,7 @@ pid_t CWindow::getPID() {
}
IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) {
for (auto& wd : m_dWindowDecorations) {
for (auto const& wd : m_dWindowDecorations) {
if (wd->getDecorationType() == type)
return wd.get();
}
@ -407,6 +408,11 @@ void CWindow::moveToWorkspace(PHLWORKSPACE 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;
setAnimationsToMove();
@ -445,16 +451,22 @@ PHLWINDOW CWindow::X11TransientFor() {
if (!m_pXWaylandSurface || !m_pXWaylandSurface->parent)
return nullptr;
auto s = m_pXWaylandSurface->parent;
auto oldParent = s;
auto s = m_pXWaylandSurface->parent;
std::vector<SP<CXWaylandSurface>> visited;
while (s) {
// break cyclic loop of m_pXWaylandSurface being parent of itself, #TODO reject this from even being created?
if (!s->parent || s->parent == oldParent)
// break loops. Some X apps make them, and it seems like it's valid behavior?!?!?!
// TODO: we should reject loops being created in the first place.
if (std::find(visited.begin(), visited.end(), s) != visited.end())
break;
visited.emplace_back(s.lock());
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)
continue;
return w;
@ -464,7 +476,7 @@ PHLWINDOW CWindow::X11TransientFor() {
}
void CWindow::removeDecorationByType(eDecorationType type) {
for (auto& wd : m_dWindowDecorations) {
for (auto const& wd : m_dWindowDecorations) {
if (wd->getDecorationType() == type)
m_vDecosToRemove.push_back(wd.get());
}
@ -502,6 +514,7 @@ void CWindow::onUnmap() {
m_fAlpha.setCallbackOnEnd(unregisterVar);
m_cRealShadowColor.setCallbackOnEnd(unregisterVar);
m_fDimPercent.setCallbackOnEnd(unregisterVar);
m_fMovingToWorkspaceAlpha.setCallbackOnEnd(unregisterVar);
m_vRealSize.setCallbackOnBegin(nullptr);
@ -542,6 +555,7 @@ void CWindow::onMap() {
m_fAlpha.resetAllCallbacks();
m_cRealShadowColor.resetAllCallbacks();
m_fDimPercent.resetAllCallbacks();
m_fMovingToWorkspaceAlpha.resetAllCallbacks();
m_vRealPosition.registerVar();
m_vRealSize.registerVar();
@ -551,6 +565,7 @@ void CWindow::onMap() {
m_fAlpha.registerVar();
m_cRealShadowColor.registerVar();
m_fDimPercent.registerVar();
m_fMovingToWorkspaceAlpha.registerVar();
m_fBorderAngleAnimationProgress.setCallbackOnEnd([&](void* ptr) { onBorderAngleAnimEnd(ptr); }, false);
@ -617,7 +632,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
int opacityIDX = 0;
for (auto& r : vars) {
for (auto const& r : vars) {
if (r == "opacity")
continue;
@ -666,7 +681,7 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
return;
}
for (auto& token : colorsAndAngles) {
for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
@ -727,9 +742,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
}
m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority);
m_vRealSize =
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());
clampWindowSize(std::nullopt, m_sWindowData.maxSize.value());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r.szRule, e.what()); }
} else if (r.szRule.starts_with("minsize")) {
try {
@ -742,12 +756,14 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
}
m_sWindowData.minSize = CWindowOverridableVar(VEC, priority);
m_vRealSize =
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());
clampWindowSize(m_sWindowData.minSize.value(), std::nullopt);
if (m_sGroupData.pNextWindow.expired())
setHidden(false);
} 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.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE);
m_sWindowData.renderUnfocused.unset(PRIORITY_WINDOW_RULE);
m_eIdleInhibitMode = IDLEINHIBIT_NONE;
m_tags.removeDynamicTags();
m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock());
for (auto& r : m_vMatchedRules) {
for (auto const& r : m_vMatchedRules) {
applyDynamicRule(r);
}
@ -881,7 +899,7 @@ void CWindow::destroyGroup() {
addresses += std::format("{:x},", (uintptr_t)curr.get());
} while (curr.get() != this);
for (auto& w : members) {
for (auto const& w : members) {
if (w->m_sGroupData.head)
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr);
w->m_sGroupData.head = false;
@ -889,7 +907,7 @@ void CWindow::destroyGroup() {
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked;
g_pKeybindManager->m_bGroupsLocked = true;
for (auto& w : members) {
for (auto const& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
w->updateWindowDecos();
}
@ -937,12 +955,16 @@ int CWindow::getGroupSize() {
}
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
&& ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or
|| (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked
&& !(m_sGroupData.pNextWindow.lock() && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group
&& !m_sGroupData.deny // source is not denied entry
&& !(m_eGroupRules & GROUP_BARRED && m_bFirstMap); // group rule doesn't prevent adding window
&& !(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) {
@ -973,7 +995,7 @@ void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
const auto PCURRENT = getGroupCurrent();
const bool FULLSCREEN = PCURRENT->isFullscreen();
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 PWINDOWPOS = PCURRENT->m_vRealPosition.goal();
@ -1235,6 +1257,16 @@ int CWindow::surfacesCount() {
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() {
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;
}
int CWindow::workspaceID() {
WORKSPACEID CWindow::workspaceID() {
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);
for (auto& e : envs) {
for (auto const& e : envs) {
if (!e.contains('='))
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)))
return;
if (!m_bIsMapped) {
Debug::log(LOG, "Ignoring CWindow::activate focus/warp, window is not mapped yet.");
return;
}
if (m_bIsFloating)
g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true);
@ -1511,7 +1548,7 @@ PHLWINDOW CWindow::getSwallower() {
if (!currentPid)
break;
for (auto& w : g_pCompositor->m_vWindows) {
for (auto const& w : g_pCompositor->m_vWindows) {
if (!w->m_bIsMapped || w->isHidden())
continue;
@ -1536,7 +1573,7 @@ PHLWINDOW CWindow::getSwallower() {
return candidates.at(0);
// 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)
continue;
@ -1556,3 +1593,11 @@ void CWindow::unsetWindowData(eOverridePriority 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> tearing = false;
CWindowOverridableVar<bool> xray = false;
CWindowOverridableVar<bool> renderUnfocused = false;
CWindowOverridableVar<int> rounding;
CWindowOverridableVar<int> borderSize;
@ -196,13 +197,14 @@ struct SWindowRule {
std::string szInitialTitle;
std::string szInitialClass;
std::string szTag;
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
int bX11 = -1; // -1 means "ANY"
int bFloating = -1;
int bFullscreen = -1;
int bPinned = -1;
int bFocus = -1;
std::string szFullscreenState = ""; // empty means any
std::string szOnWorkspace = ""; // empty means any
std::string szWorkspace = ""; // empty means any
};
struct SInitialWorkspaceToken {
@ -270,7 +272,7 @@ class CWindow {
bool m_bDraggingTiled = false; // for dragging around tiled windows
bool m_bWasMaximized = false;
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_szClass = "";
std::string m_szInitialTitle = "";
@ -287,8 +289,6 @@ class CWindow {
// XWayland stuff
bool m_bIsX11 = false;
PHLWINDOWREF m_pX11Parent;
uint64_t m_iX11Type = 0;
bool m_bIsModal = false;
bool m_bX11DoesntWantBorders = false;
bool m_bX11ShouldntFocus = false;
float m_fX11SurfaceScaledBy = 1.f;
@ -351,6 +351,10 @@ class CWindow {
// animated tint
CAnimatedVariable<float> m_fDimPercent;
// animate moving to an invisible workspace
int m_iMonitorMovedFrom = -1; // -1 means not moving
CAnimatedVariable<float> m_fMovingToWorkspaceAlpha;
// swallowing
PHLWINDOWREF m_pSwallowed;
@ -358,8 +362,8 @@ class CWindow {
bool m_bStayFocused = false;
// for toplevel monitor events
uint64_t m_iLastToplevelMonitorID = -1;
uint64_t m_iLastSurfaceMonitorID = -1;
MONITORID m_iLastToplevelMonitorID = -1;
MONITORID m_iLastSurfaceMonitorID = -1;
// for idle inhibiting windows
eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE;
@ -421,10 +425,11 @@ class CWindow {
bool canBeTorn();
void setSuspended(bool suspend);
bool visibleOnMonitor(CMonitor* pMonitor);
int workspaceID();
WORKSPACEID workspaceID();
bool onSpecialWorkspace();
void activate(bool force = false);
int surfacesCount();
void clampWindowSize(const std::optional<Vector2D> minSize, const std::optional<Vector2D> maxSize);
bool isFullscreen();
bool isEffectiveInternalFSMode(const eFullscreenMode);
@ -463,6 +468,8 @@ class CWindow {
void warpCursor();
PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect();
bool isModal();
// listeners
void onAck(uint32_t serial);
@ -490,9 +497,9 @@ class CWindow {
private:
// For hidden windows and stuff
bool m_bHidden = false;
bool m_bSuspended = false;
int m_iLastWorkspace = WORKSPACE_INVALID;
bool m_bHidden = false;
bool m_bSuspended = false;
WORKSPACEID m_iLastWorkspace = WORKSPACE_INVALID;
};
inline bool valid(PHLWINDOW w) {

View file

@ -5,27 +5,28 @@
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
PHLWORKSPACE CWorkspace::create(int id, int monitorID, std::string name, bool special, bool isEmtpy) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmtpy);
PHLWORKSPACE CWorkspace::create(WORKSPACEID id, MONITORID monitorID, std::string name, bool special, bool isEmpty) {
PHLWORKSPACE workspace = makeShared<CWorkspace>(id, monitorID, name, special, isEmpty);
workspace->init(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_iID = id;
m_szName = name;
m_bIsSpecialWorkspace = special;
m_bWasCreatedEmtpy = isEmtpy;
m_bWasCreatedEmpty = isEmpty;
}
void CWorkspace::init(PHLWORKSPACE 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);
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);
m_fAlpha.setValueAndWarp(1.f);
@ -48,9 +49,9 @@ void CWorkspace::init(PHLWORKSPACE self) {
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
m_bPersistent = WORKSPACERULE.isPersistent;
if (self->m_bWasCreatedEmtpy)
if (self->m_bWasCreatedEmpty)
if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd)
g_pKeybindManager->spawn(*cmd);
g_pKeybindManager->spawnWithRules(*cmd, self);
g_pEventManager->postEvent({"createworkspace", 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) {
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;
static auto PWORKSPACEGAP = CConfigValue<Hyprlang::INT>("general:gaps_workspaces");
// set floating windows offset callbacks
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)
continue;
@ -190,7 +198,7 @@ void CWorkspace::setActive(bool on) {
; // 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
}
@ -275,7 +283,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
i = std::min(NEXTSPACE, std::string::npos - 1);
if (cur == 'r') {
int from = 0, to = 0;
WORKSPACEID from = 0, to = 0;
if (!prop.starts_with("r[") || !prop.ends_with("]")) {
Debug::log(LOG, "Invalid selector {}", selector);
return false;
@ -365,7 +373,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
}
if (cur == 'w') {
int from = 0, to = 0;
WORKSPACEID from = 0, to = 0;
if (!prop.starts_with("w[") || !prop.ends_with("]")) {
Debug::log(LOG, "Invalid selector {}", selector);
return false;
@ -378,7 +386,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
bool wantsCountVisible = false;
int flagCount = 0;
for (auto& flag : prop) {
for (auto const& flag : prop) {
if (flag == 't' && wantsOnlyTiled == -1) {
wantsOnlyTiled = 1;
flagCount++;
@ -446,7 +454,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
return false;
}
int count;
WORKSPACEID count;
if (wantsCountGroup)
count = g_pCompositor->getGroupsOnWorkspace(m_iID, wantsOnlyTiled == -1 ? std::nullopt : std::optional<bool>((bool)wantsOnlyTiled),
wantsCountVisible ? std::optional<bool>(wantsCountVisible) : std::nullopt);
@ -506,7 +514,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) {
void CWorkspace::markInert() {
m_bInert = true;
m_iID = WORKSPACE_INVALID;
m_iMonitorID = -1;
m_iMonitorID = MONITOR_INVALID;
m_bVisible = false;
}

View file

@ -17,16 +17,16 @@ class CWindow;
class CWorkspace {
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
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();
// Workspaces ID-based have IDs > 0
// and workspaces name-based have IDs starting with -1337
int m_iID = -1;
WORKSPACEID m_iID = WORKSPACE_INVALID;
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
// to the previous workspace.
SWorkspaceIDName m_sPrevWorkspace, m_sPrevWorkspacePerMonitor;
@ -57,7 +57,7 @@ class CWorkspace {
// last monitor (used on reconnect)
std::string m_szLastMonitor = "";
bool m_bWasCreatedEmtpy = true;
bool m_bWasCreatedEmpty = true;
bool m_bPersistent = false;
@ -67,7 +67,7 @@ class CWorkspace {
void startAnim(bool in, bool left, bool instant = false);
void setActive(bool on);
void moveToMonitor(const int&);
void moveToMonitor(const MONITORID&);
PHLWINDOW getLastFocusedWindow();
void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace);

View file

@ -119,7 +119,8 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) {
if (IDX != XKB_MOD_INVALID)
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) {
@ -177,13 +178,18 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbState)
xkb_state_unref(xkbState);
if (xkbSymState)
xkb_state_unref(xkbSymState);
xkbState = nullptr;
xkbStaticState = nullptr;
xkbSymState = nullptr;
if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this);
xkbStaticState = xkb_state_new(keymap);
xkbState = xkb_state_new(keymap);
xkbSymState = xkb_state_new(keymap);
return;
}
@ -229,6 +235,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(KEYMAP);
xkbStaticState = xkb_state_new(KEYMAP);
xkbSymState = xkb_state_new(KEYMAP);
xkb_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT);
@ -251,6 +258,7 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
xkbState = xkb_state_new(NEWKEYMAP);
xkbStaticState = xkb_state_new(NEWKEYMAP);
xkbSymState = xkb_state_new(NEWKEYMAP);
xkb_keymap_unref(NEWKEYMAP);
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);
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, group);
if (!updateModifiersState())
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);
if (updateModifiersState()) {
if (xkbSymState)
xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, modifiersState.group);
keyboardEvents.modifiers.emit(SModifiersEvent{
.depressed = modifiersState.depressed,
.latched = modifiersState.latched,

View file

@ -1,7 +1,6 @@
#pragma once
#include "IHID.hpp"
#include "../helpers/WLListener.hpp"
#include "../macros.hpp"
#include "../helpers/math/Math.hpp"
@ -83,8 +82,9 @@ class IKeyboard : public IHID {
bool keymapOverridden = false;
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_keymap* xkbKeymap = 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;
struct {
uint32_t depressed = 0, latched = 0, locked = 0, group = 0;

View file

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

View file

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

View file

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

View file

@ -27,7 +27,6 @@ namespace Events {
// Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);
DYNLISTENFUNC(monitorStateRequest);
DYNLISTENFUNC(monitorDamage);
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) {
const auto PMONITOR = (CMonitor*)owner;

View file

@ -12,6 +12,7 @@
#include "../protocols/core/Compositor.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../xwayland/XSurface.hpp"
#include "managers/PointerManager.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
@ -120,7 +121,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
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)
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))
requestedClientFSMode = FSMODE_FULLSCREEN;
for (auto& r : PWINDOW->m_vMatchedRules) {
for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("monitor")) {
try {
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;
} else {
if (isNumber(MONITORSTR)) {
const long int MONITOR = std::stoi(MONITORSTR);
const MONITORID MONITOR = std::stoi(MONITORSTR);
if (!g_pCompositor->getMonitorFromID(MONITOR))
PWINDOW->m_iMonitorID = 0;
else
@ -322,11 +323,11 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->updateWindowData();
if (PWINDOW->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(PWINDOW);
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
PWINDOW->m_bCreatedOverFullscreen = true;
// size and move rules
for (auto& r : PWINDOW->m_vMatchedRules) {
for (auto const& r : PWINDOW->m_vMatchedRules) {
if (r.szRule.starts_with("size")) {
try {
const auto VALUE = r.szRule.substr(r.szRule.find(' ') + 1);
@ -344,7 +345,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
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());
PWINDOW->setHidden(false);
@ -449,8 +451,36 @@ void Events::listener_mapWindow(void* owner, void* data) {
} else {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
// Set the pseudo size here too so that it doesnt end up being 0x0
PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize.goal() - Vector2D(10, 10);
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);
}
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow.lock();
@ -477,7 +507,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
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_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA);
@ -577,7 +607,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->updateWorkspaceWindows(PWINDOW->workspaceID());
if (PMONITOR && PWINDOW->m_iX11Type == 2)
if (PMONITOR && PWINDOW->isX11OverrideRedirect())
PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale;
}
@ -648,7 +678,12 @@ void Events::listener_unmapWindow(void* owner, void* data) {
// refocus on a new window if needed
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);
@ -717,33 +752,28 @@ void Events::listener_commitWindow(void* owner, void* data) {
const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->current.minSize;
const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->current.maxSize;
if (MAXSIZE > Vector2D{1, 1}) {
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);
}
PWINDOW->clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional<Vector2D>{MAXSIZE} : std::nullopt);
g_pHyprRenderer->damageWindow(PWINDOW);
}
if (!PWINDOW->m_pWorkspace->m_bVisible)
return;
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);
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,
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) {
PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces();
@ -751,7 +781,6 @@ void Events::listener_commitWindow(void* owner, void* data) {
}
// 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) {
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);
if (PWINDOW->m_iX11Type == 2) {
if (PWINDOW->isX11OverrideRedirect()) {
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));
for (auto& p : *pVec) {
for (auto const& p : *pVec) {
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);
}
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;
}

View file

@ -8,9 +8,10 @@ class CColor {
CColor(float r, float g, float b, float a);
CColor(uint64_t);
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 {
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) {
for (auto& fmt : GLES3_FORMATS) {
for (auto const& fmt : GLES3_FORMATS) {
if (fmt.drmFormat == drm)
return &fmt;
}
@ -261,7 +261,7 @@ const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) {
}
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)
return &fmt;
}

View file

@ -180,7 +180,7 @@ void handleNoop(struct wl_listener* listener, void* data) {
std::string escapeJSONStrings(const std::string& str) {
std::ostringstream oss;
for (auto& c : str) {
for (auto const& c : str) {
switch (c) {
case '"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break;
@ -249,17 +249,17 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
return {WORKSPACE_INVALID};
}
std::set<int> invalidWSes;
std::set<WORKSPACEID> invalidWSes;
if (same_mon) {
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) {
for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) {
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID))
invalidWSes.insert(rule.workspaceId);
}
}
int id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0;
while (++id < INT_MAX) {
WORKSPACEID id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0;
while (++id < LONG_MAX) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
if (!invalidWSes.contains(id) && (!PWORKSPACE || g_pCompositor->getWindowsOnWorkspace(id) == 0)) {
result.id = id;
@ -296,18 +296,18 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
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.
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)) {
// Can't jump to this workspace
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);
if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) {
// Can't be invalid
@ -318,8 +318,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
}
// Prepare all named workspaces in case when we need them
std::vector<int> namedWSes;
for (auto& ws : g_pCompositor->m_vWorkspaces) {
std::vector<WORKSPACEID> namedWSes;
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0)
continue;
@ -347,19 +347,19 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
} else {
// 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;
int predictedWSID = activeWSID + remains;
int remainingWSes = 0;
char walkDir = in[1];
WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1;
WORKSPACEID predictedWSID = activeWSID + remains;
int remainingWSes = 0;
char walkDir = in[1];
// 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)
int beginID = in[1] == '+' ? activeWSID + 1 : predictedWSID;
int endID = in[1] == '+' ? predictedWSID : activeWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
WORKSPACEID beginID = in[1] == '+' ? activeWSID + 1 : predictedWSID;
WORKSPACEID endID = in[1] == '+' ? predictedWSID : activeWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; it != invalidWSes.end() && *it <= endID; it++) {
remainingWSes++;
}
@ -367,7 +367,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
if (activeWSID < 0) {
// Behaviour similar to 'm'
// Find current
int currentItem = -1;
size_t currentItem = -1;
for (size_t i = 0; i < namedWSes.size(); i++) {
if (namedWSes[i] == activeWSID) {
currentItem = i;
@ -376,15 +376,15 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
}
currentItem += remains;
currentItem = std::max(currentItem, 0);
if (currentItem >= (int)namedWSes.size()) {
currentItem = std::max(currentItem, static_cast<size_t>(0));
if (currentItem >= namedWSes.size()) {
// At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0
int diff = currentItem - (namedWSes.size() - 1);
predictedWSID = diff;
int beginID = 1;
int endID = predictedWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
size_t diff = currentItem - (namedWSes.size() - 1);
predictedWSID = diff;
WORKSPACEID beginID = 1;
WORKSPACEID endID = predictedWSID;
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
for (auto it = begin; it != invalidWSes.end() && *it <= endID; it++) {
remainingWSes++;
}
walkDir = '+';
@ -397,10 +397,10 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// Go in the search direction for remainingWSes
// The performance impact is directly proportional to the number of open and bound workspaces
int finalWSID = predictedWSID;
WORKSPACEID finalWSID = predictedWSID;
if (walkDir == '-') {
int beginID = finalWSID;
int curID = finalWSID;
WORKSPACEID beginID = finalWSID;
WORKSPACEID curID = finalWSID;
while (--curID > 0 && remainingWSes > 0) {
if (!invalidWSes.contains(curID)) {
remainingWSes--;
@ -411,9 +411,9 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
if (namedWSes.size()) {
// Go to the named workspaces
// Need remainingWSes more
int namedWSIdx = namedWSes.size() - remainingWSes;
auto namedWSIdx = namedWSes.size() - remainingWSes;
// 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];
} else {
// 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 == '+') {
int curID = finalWSID;
WORKSPACEID curID = finalWSID;
while (++curID < INT32_MAX && remainingWSes > 0) {
if (!invalidWSes.contains(curID)) {
remainingWSes--;
@ -460,10 +460,10 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
result.id = (int)PLUSMINUSRESULT.value();
// result now has +/- what we should move on mon
int remains = (int)result.id;
int remains = (int)result.id;
std::vector<int> validWSes;
for (auto& ws : g_pCompositor->m_vWorkspaces) {
std::vector<WORKSPACEID> validWSes;
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID && !onAllMonitors))
continue;
@ -472,7 +472,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
std::sort(validWSes.begin(), validWSes.end());
int currentItem = -1;
ssize_t currentItem = -1;
if (absolute) {
// 1-index
@ -481,7 +481,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
// clamp
if (currentItem < 0) {
currentItem = 0;
} else if (currentItem >= (int)validWSes.size()) {
} else if (currentItem >= (ssize_t)validWSes.size()) {
currentItem = validWSes.size() - 1;
}
} else {
@ -489,8 +489,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
remains = remains < 0 ? -((-remains) % validWSes.size()) : remains % validWSes.size();
// get the current item
int activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1;
for (size_t i = 0; i < validWSes.size(); i++) {
WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1;
for (ssize_t i = 0; i < (ssize_t)validWSes.size(); i++) {
if (validWSes[i] == activeWSID) {
currentItem = i;
break;
@ -501,7 +501,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) {
currentItem += remains;
// sanitize
if (currentItem >= (int)validWSes.size()) {
if (currentItem >= (ssize_t)validWSes.size()) {
currentItem = currentItem % validWSes.size();
} else if (currentItem < 0) {
currentItem = validWSes.size() + currentItem;
@ -547,9 +547,9 @@ std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceNa
const std::string workspaceRule = "workspace " + inWorkspaceName;
if (cmd[0] == '[') {
const int closingBracketIdx = cmd.find_last_of(']');
auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
cmd = cmd.substr(closingBracketIdx + 1);
const auto closingBracketIdx = cmd.find_last_of(']');
auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
cmd = cmd.substr(closingBracketIdx + 1);
auto rulesList = CVarList(tmpRules, 0, ';');
@ -628,27 +628,6 @@ void logSystemInfo() {
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) {
#if defined(KERN_PROC_PID)
int mib[] = {
@ -785,13 +764,13 @@ std::vector<SCallstackFrameInfo> getBacktrace() {
#ifdef HAS_EXECINFO
void* bt[1024];
size_t btSize;
int btSize;
char** btSymbols;
btSize = backtrace(bt, 1024);
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]}});
}
#else

View file

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

View file

@ -1,6 +1,7 @@
#include "Monitor.hpp"
#include "MiscFunctions.hpp"
#include "math/Math.hpp"
#include "sync/SyncReleaser.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/GammaControl.hpp"
@ -8,13 +9,18 @@
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp>
#include "debug/Log.hpp"
#include <hyprutils/string/String.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data);
@ -22,7 +28,7 @@ int ratHandler(void* data) {
return 1;
}
CMonitor::CMonitor() : state(this) {
CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : state(this), output(output_) {
;
}
@ -31,20 +37,34 @@ CMonitor::~CMonitor() {
}
void CMonitor::onConnect(bool noRule) {
CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }};
if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
}
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.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); });
listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); });
listeners.needsFrame =
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) {
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) {
@ -77,6 +97,7 @@ void CMonitor::onConnect(bool noRule) {
tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;
if (m_bEnabled) {
output->state->resetExplicitFences();
output->state->setEnabled(true);
state.commit();
return;
@ -96,11 +117,12 @@ void CMonitor::onConnect(bool noRule) {
createdByUser = true; // should be true. WL and Headless backends should be addable / removable
// 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 (monitorRule.disabled) {
output->state->resetExplicitFences();
output->state->setEnabled(false);
if (!state.commit())
@ -137,6 +159,7 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true;
output->state->resetExplicitFences();
output->state->setEnabled(true);
// set mode, also applies
@ -152,7 +175,7 @@ void CMonitor::onConnect(bool noRule) {
setupDefaultWS(monitorRule);
for (auto& ws : g_pCompositor->m_vWorkspaces) {
for (auto const& ws : g_pCompositor->m_vWorkspaces) {
if (!valid(ws))
continue;
@ -173,10 +196,6 @@ void CMonitor::onConnect(bool noRule) {
if (!activeMonitorRule.mirrorOf.empty())
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
g_pCompositor->setActiveMonitor(this);
@ -188,7 +207,7 @@ void CMonitor::onConnect(bool noRule) {
// verify last mon valid
bool found = false;
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m == g_pCompositor->m_pLastMonitor) {
found = true;
break;
@ -205,9 +224,20 @@ void CMonitor::onConnect(bool noRule) {
PROTO::gamma->applyGammaToState(this);
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) {
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) {
wl_event_source_remove(renderTimer);
@ -223,7 +253,7 @@ void CMonitor::onDisconnect(bool destroy) {
// Cleanup everything. Move windows back, snap cursor, shit.
CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
break;
@ -240,7 +270,7 @@ void CMonitor::onDisconnect(bool destroy) {
}
if (!mirrors.empty()) {
for (auto& m : mirrors) {
for (auto const& m : mirrors) {
m->setMirror("");
}
@ -253,7 +283,7 @@ void CMonitor::onDisconnect(bool destroy) {
listeners.commit.reset();
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)
ls->layerSurface->sendClosed();
}
@ -262,9 +292,6 @@ void CMonitor::onDisconnect(bool destroy) {
Debug::log(LOG, "Removed monitor {}!", szName);
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
if (!BACKUPMON) {
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
g_pCompositor->enterUnsafeState();
@ -279,13 +306,13 @@ void CMonitor::onDisconnect(bool destroy) {
// move workspaces
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)) {
wspToMove.push_back(w);
}
}
for (auto& w : wspToMove) {
for (auto const& w : wspToMove) {
w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
@ -300,6 +327,7 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace->m_bVisible = false;
activeWorkspace.reset();
output->state->resetExplicitFences();
output->state->setEnabled(false);
if (!state.commit())
@ -312,7 +340,7 @@ void CMonitor::onDisconnect(bool destroy) {
int mostHz = 0;
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) {
pMonitorMostHz = m.get();
mostHz = m->refreshRate;
@ -357,7 +385,7 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
*PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
// 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.damageEntire();
return false;
@ -382,8 +410,8 @@ bool CMonitor::matchesStaticSelector(const std::string& selector) const {
}
}
int CMonitor::findAvailableDefaultWS() {
for (size_t i = 1; i < INT32_MAX; ++i) {
WORKSPACEID CMonitor::findAvailableDefaultWS() {
for (WORKSPACEID i = 1; i < LONG_MAX; ++i) {
if (g_pCompositor->getWorkspaceByID(i))
continue;
@ -393,7 +421,7 @@ int CMonitor::findAvailableDefaultWS() {
return i;
}
return INT32_MAX; // shouldn't be reachable
return LONG_MAX; // shouldn't be reachable
}
void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
@ -468,7 +496,7 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
pMirrorOf = nullptr;
// set rule
const auto RULE = g_pConfigManager->getMonitorRuleFor(*this);
const auto RULE = g_pConfigManager->getMonitorRuleFor(self.lock());
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
} else {
CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) {
for (auto const& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
break;
@ -505,13 +533,13 @@ void CMonitor::setMirror(const std::string& mirrorOf) {
// move all the WS
std::deque<PHLWORKSPACE> wspToMove;
for (auto& w : g_pCompositor->m_vWorkspaces) {
for (auto const& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
wspToMove.push_back(w);
}
}
for (auto& w : wspToMove) {
for (auto const& w : wspToMove) {
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
}
@ -585,7 +613,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
pWorkspace->startAnim(true, ANIMTOLEFT);
// 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)
w->moveToWorkspace(pWorkspace);
}
@ -631,7 +659,7 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
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);
}
@ -694,14 +722,14 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (animate)
pWorkspace->startAnim(true, true);
for (auto& w : g_pCompositor->m_vWindows) {
for (auto const& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == pWorkspace) {
w->m_iMonitorID = ID;
w->updateSurfaceScaleTransformDetails();
w->setAnimationsToMove();
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
const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE);
Vector2D pos = w->m_vRealPosition.goal();
@ -738,7 +766,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
g_pCompositor->updateSuspendedStates();
}
void CMonitor::setSpecialWorkspace(const int& id) {
void CMonitor::setSpecialWorkspace(const WORKSPACEID& id) {
setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id));
}
@ -751,19 +779,16 @@ Vector2D CMonitor::middle() {
}
void CMonitor::updateMatrix() {
matrixIdentity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
matrixTranslate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
matrixTransform(projMatrix.data(), wlTransformToHyprutils(transform));
matrixTranslate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
projMatrix = Mat3x3::identity();
if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
projMatrix.translate(vecPixelSize / 2.0).transform(wlTransformToHyprutils(transform)).translate(-vecTransformedSize / 2.0);
}
int64_t CMonitor::activeWorkspaceID() {
WORKSPACEID CMonitor::activeWorkspaceID() {
return activeWorkspace ? activeWorkspace->m_iID : 0;
}
int64_t CMonitor::activeSpecialWorkspaceID() {
WORKSPACEID CMonitor::activeSpecialWorkspaceID() {
return activeSpecialWorkspace ? activeSpecialWorkspace->m_iID : 0;
}
@ -771,20 +796,28 @@ CBox CMonitor::logicalBox() {
return {vecPosition, vecSize};
}
static void onDoneSource(void* data) {
auto pMonitor = (CMonitor*)data;
if (!PROTO::outputs.contains(pMonitor->szName))
void CMonitor::scheduleDone() {
if (doneScheduled)
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() {
if (doneSource)
return;
doneSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::onDoneSource, this);
void CMonitor::setCTM(const Mat3x3& ctm_) {
ctm = ctm_;
ctmUpdated = true;
g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME);
}
bool CMonitor::attemptDirectScanout() {
@ -808,28 +841,92 @@ bool CMonitor::attemptDirectScanout() {
if (!PSURFACE->current.buffer || !PSURFACE->current.texture || !PSURFACE->current.texture->m_pEglImage /* dmabuf */)
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
// 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->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);
if (!state.test())
if (!state.test()) {
Debug::log(TRACE, "attemptDirectScanout: failed basic test");
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;
clock_gettime(CLOCK_MONOTONIC, &now);
Debug::log(TRACE, "presentFeedback for DS");
PSURFACE->presentFeedback(&now, this, true);
PSURFACE->presentFeedback(&now, self.lock());
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()) {
lastScanout = PCANDIDATE;
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 {
Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface");
lastScanout.reset();
return false;
}
@ -837,6 +934,11 @@ bool CMonitor::attemptDirectScanout() {
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) {
m_pOwner = owner;
}
@ -846,17 +948,20 @@ CMonitorState::~CMonitorState() {
}
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");
return;
}
if (m_pOwner->output->state->state().buffer)
return;
if (STATE.buffer) {
if (const auto params = STATE.buffer->dmabuf(); params.success && params.format == m_pOwner->drmFormat)
return;
}
// 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
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->swapchain->rollback(); // restore the counter, don't advance the swapchain
}
@ -890,7 +995,7 @@ bool CMonitorState::updateSwapchain() {
Debug::log(WARN, "updateSwapchain: No mode?");
return true;
}
options.format = STATE.drmFormat;
options.format = m_pOwner->drmFormat;
options.scanout = true;
options.length = 2;
options.size = MODE->pixelSize;

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