├── .clang-format ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug.yml │ └── feature.yml ├── pull_request_template.md └── workflows │ ├── ci.yaml │ ├── man-update.yaml │ ├── nix-build.yml │ ├── nix-ci.yml │ ├── nix-update-inputs.yml │ ├── nix-update-wlroots.yml │ ├── release.yaml │ └── security-checks.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Makefile ├── README.md ├── assets ├── header.svg ├── hyprland-portals.conf ├── hyprland.png ├── meson.build ├── wall_2K.png ├── wall_4K.png ├── wall_8K.png ├── wall_anime2_2K.png ├── wall_anime2_4K.png ├── wall_anime2_8K.png ├── wall_anime_2K.png ├── wall_anime_4K.png └── wall_anime_8K.png ├── docs ├── Hyprland.1 ├── Hyprland.1.rst ├── ISSUE_GUIDELINES.md ├── hyprctl.1 ├── hyprctl.1.rst └── meson.build ├── example ├── examplePlugin │ ├── Makefile │ ├── customDecoration.cpp │ ├── customDecoration.hpp │ ├── customLayout.cpp │ ├── customLayout.hpp │ ├── globals.hpp │ └── main.cpp ├── hyprland.conf ├── hyprland.desktop ├── hyprland.service ├── launch.json ├── meson.build ├── screenShader.frag └── swaybg@.service ├── flake.lock ├── flake.nix ├── hyprctl ├── CMakeLists.txt ├── Makefile ├── main.cpp └── meson.build ├── hyprland.pc.in ├── meson.build ├── meson_options.txt ├── nix ├── default.nix ├── hm-module.nix ├── module.nix ├── overlays.nix ├── patches │ ├── meson-build.patch │ └── wlroots-nvidia.patch ├── udis86.nix ├── update-inputs.sh ├── update-wlroots.sh └── wlroots.nix ├── props.json ├── protocols ├── idle.xml ├── meson.build ├── pointer-constraints-unstable-v1.xml ├── tablet-unstable-v2.xml ├── wlr-foreign-toplevel-management-unstable-v1.xml ├── wlr-layer-shell-unstable-v1.xml ├── wlr-output-power-management-unstable-v1.xml └── wlr-screencopy-unstable-v1.xml ├── scripts └── generateVersion.sh ├── src ├── Compositor.cpp ├── Compositor.hpp ├── SharedDefs.hpp ├── Window.cpp ├── Window.hpp ├── config │ ├── ConfigDataValues.hpp │ ├── ConfigManager.cpp │ ├── ConfigManager.hpp │ └── defaultConfig.hpp ├── debug │ ├── CrashReporter.cpp │ ├── CrashReporter.hpp │ ├── HyprCtl.cpp │ ├── HyprCtl.hpp │ ├── HyprDebugOverlay.cpp │ ├── HyprDebugOverlay.hpp │ ├── HyprNotificationOverlay.cpp │ ├── HyprNotificationOverlay.hpp │ ├── Log.cpp │ ├── Log.hpp │ └── TracyDefines.hpp ├── defines.hpp ├── events │ ├── Devices.cpp │ ├── Events.hpp │ ├── Layers.cpp │ ├── Misc.cpp │ ├── Monitors.cpp │ ├── Popups.cpp │ └── Windows.cpp ├── helpers │ ├── AnimatedVariable.cpp │ ├── AnimatedVariable.hpp │ ├── BezierCurve.cpp │ ├── BezierCurve.hpp │ ├── Box.cpp │ ├── Box.hpp │ ├── Color.cpp │ ├── Color.hpp │ ├── MiscFunctions.cpp │ ├── MiscFunctions.hpp │ ├── Monitor.cpp │ ├── Monitor.hpp │ ├── Region.cpp │ ├── Region.hpp │ ├── Splashes.hpp │ ├── SubsurfaceTree.cpp │ ├── SubsurfaceTree.hpp │ ├── Timer.cpp │ ├── Timer.hpp │ ├── VarList.cpp │ ├── VarList.hpp │ ├── Vector2D.cpp │ ├── Vector2D.hpp │ ├── WLClasses.cpp │ ├── WLClasses.hpp │ ├── WLListener.cpp │ ├── WLListener.hpp │ ├── WLSurface.cpp │ ├── WLSurface.hpp │ ├── Watchdog.cpp │ ├── Watchdog.hpp │ ├── Workspace.cpp │ ├── Workspace.hpp │ ├── X11Stubs.hpp │ └── XWaylandStubs.hpp ├── hyprerror │ ├── HyprError.cpp │ └── HyprError.hpp ├── includes.hpp ├── init │ ├── initHelpers.cpp │ └── initHelpers.hpp ├── layout │ ├── DwindleLayout.cpp │ ├── DwindleLayout.hpp │ ├── IHyprLayout.cpp │ ├── IHyprLayout.hpp │ ├── MasterLayout.cpp │ └── MasterLayout.hpp ├── macros.hpp ├── main.cpp ├── managers │ ├── AnimationManager.cpp │ ├── AnimationManager.hpp │ ├── EventManager.cpp │ ├── EventManager.hpp │ ├── HookSystemManager.cpp │ ├── HookSystemManager.hpp │ ├── KeybindManager.cpp │ ├── KeybindManager.hpp │ ├── LayoutManager.cpp │ ├── LayoutManager.hpp │ ├── ProtocolManager.cpp │ ├── ProtocolManager.hpp │ ├── SessionLockManager.cpp │ ├── SessionLockManager.hpp │ ├── ThreadManager.cpp │ ├── ThreadManager.hpp │ ├── XWaylandManager.cpp │ ├── XWaylandManager.hpp │ └── input │ │ ├── IdleInhibitor.cpp │ │ ├── InputManager.cpp │ │ ├── InputManager.hpp │ │ ├── InputMethodRelay.cpp │ │ ├── InputMethodRelay.hpp │ │ ├── Swipe.cpp │ │ ├── Tablets.cpp │ │ └── Touch.cpp ├── meson.build ├── pch │ └── pch.hpp ├── plugins │ ├── HookSystem.cpp │ ├── HookSystem.hpp │ ├── PluginAPI.cpp │ ├── PluginAPI.hpp │ ├── PluginSystem.cpp │ └── PluginSystem.hpp ├── protocols │ ├── FractionalScale.cpp │ ├── FractionalScale.hpp │ ├── GlobalShortcuts.cpp │ ├── GlobalShortcuts.hpp │ ├── Screencopy.cpp │ ├── Screencopy.hpp │ ├── TextInputV1.cpp │ ├── TextInputV1.hpp │ ├── ToplevelExport.cpp │ ├── ToplevelExport.hpp │ ├── ToplevelExportWlrFuncs.hpp │ ├── WaylandProtocol.cpp │ ├── WaylandProtocol.hpp │ ├── XDGOutput.cpp │ └── XDGOutput.hpp ├── render │ ├── Framebuffer.cpp │ ├── Framebuffer.hpp │ ├── OpenGL.cpp │ ├── OpenGL.hpp │ ├── Renderer.cpp │ ├── Renderer.hpp │ ├── Shader.cpp │ ├── Shader.hpp │ ├── Shaders.hpp │ ├── Texture.cpp │ ├── Texture.hpp │ ├── Transformer.cpp │ ├── Transformer.hpp │ ├── decorations │ │ ├── CHyprDropShadowDecoration.cpp │ │ ├── CHyprDropShadowDecoration.hpp │ │ ├── CHyprGroupBarDecoration.cpp │ │ ├── CHyprGroupBarDecoration.hpp │ │ ├── IHyprWindowDecoration.cpp │ │ └── IHyprWindowDecoration.hpp │ └── shaders │ │ ├── Border.hpp │ │ ├── Shadow.hpp │ │ └── Textures.hpp └── version.h.in └── subprojects ├── packagefiles └── wlroots-meson-build.patch └── wlroots.wrap /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | 5 | AccessModifierOffset: -2 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveMacros: true 8 | AlignConsecutiveAssignments: true 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: true 16 | AllowShortCaseLabelsOnASingleLine: true 17 | AllowShortFunctionsOnASingleLine: Empty 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BreakBeforeBraces: Attach 26 | BreakBeforeTernaryOperators: false 27 | BreakConstructorInitializers: AfterColon 28 | ColumnLimit: 180 29 | CompactNamespaces: false 30 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 31 | ExperimentalAutoDetectBinPacking: false 32 | FixNamespaceComments: false 33 | IncludeBlocks: Preserve 34 | IndentCaseLabels: true 35 | IndentWidth: 4 36 | PointerAlignment: Left 37 | ReflowComments: false 38 | SortIncludes: false 39 | SortUsingDeclarations: false 40 | SpaceAfterCStyleCast: false 41 | SpaceAfterLogicalNot: false 42 | SpaceAfterTemplateKeyword: true 43 | SpaceBeforeCtorInitializerColon: true 44 | SpaceBeforeInheritanceColon: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceBeforeRangeBasedForLoopColon: true 47 | SpaceInEmptyParentheses: false 48 | SpacesBeforeTrailingComments: 1 49 | SpacesInAngles: false 50 | SpacesInCStyleCastParentheses: false 51 | SpacesInContainerLiterals: false 52 | SpacesInParentheses: false 53 | SpacesInSquareBrackets: false 54 | Standard: Auto 55 | TabWidth: 4 56 | UseTab: Never 57 | 58 | AllowShortEnumsOnASingleLine: false 59 | 60 | BraceWrapping: 61 | AfterEnum: false 62 | 63 | AlignConsecutiveDeclarations: AcrossEmptyLines 64 | 65 | NamespaceIndentation: All 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: vaxry 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Something is not working right 3 | labels: ["bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before opening a new issue, take a moment to search through the current open ones. 9 | 10 | --- 11 | 12 | - type: input 13 | id: ver 14 | attributes: 15 | label: Hyprland Version 16 | description: "Paste here the output of `hyprctl version`." 17 | placeholder: Hyprland, built from branch main at commit... 18 | validations: 19 | required: true 20 | 21 | - type: dropdown 22 | id: type 23 | attributes: 24 | label: Bug or Regression? 25 | description: Is this a bug or a regression? 26 | options: 27 | - Bug 28 | - Regression 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | id: desc 34 | attributes: 35 | label: Description 36 | description: "What went wrong?" 37 | validations: 38 | required: true 39 | 40 | - type: textarea 41 | id: repro 42 | attributes: 43 | label: How to reproduce 44 | description: "How can someone else reproduce the issue?" 45 | validations: 46 | required: true 47 | 48 | - type: textarea 49 | id: logs 50 | attributes: 51 | label: Crash reports, logs, images, videos 52 | description: | 53 | Anything that can help. Please always ATTACH and not paste them. 54 | Logs can be found in /tmp/hypr 55 | Crash reports are stored in ~/.hyprland or $XDG_CACHE_HOME/hyprland 56 | 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: I'd like to request additional functionality 3 | labels: ["enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before opening a new issue, take a moment to search through the current open ones. 9 | 10 | --- 11 | 12 | - type: textarea 13 | id: desc 14 | attributes: 15 | label: Description 16 | description: "Describe your idea" 17 | validations: 18 | required: true 19 | 20 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | #### Describe your PR, what does it fix/add? 2 | 3 | 4 | #### Is there anything you want to mention? (unchecked code, possible bugs, found problems, breaking compatibility, etc.) 5 | 6 | 7 | #### Is it ready for merging, or does it need work? 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/man-update.yaml: -------------------------------------------------------------------------------- 1 | name: Build man pages 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | paths: 7 | - docs/** 8 | branches: 9 | - 'main' 10 | 11 | jobs: 12 | main: 13 | name: Build man pages 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Install deps 17 | run: sudo apt install pandoc 18 | 19 | - name: Clone repository 20 | uses: actions/checkout@v3 21 | with: 22 | token: ${{ secrets.PAT }} 23 | 24 | - name: Build man pages 25 | run: make man 26 | 27 | - uses: stefanzweifel/git-auto-commit-action@v4 28 | name: Commit 29 | with: 30 | commit_message: "[gha] build man pages" 31 | -------------------------------------------------------------------------------- /.github/workflows/nix-build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | secrets: 4 | CACHIX_AUTH_TOKEN: 5 | required: false 6 | 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | package: 12 | - hyprland 13 | - hyprland-nvidia 14 | - xdg-desktop-portal-hyprland 15 | 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Clone repository 19 | uses: actions/checkout@v3 20 | with: 21 | ref: ${{ github.ref }} 22 | 23 | - uses: DeterminateSystems/nix-installer-action@main 24 | - uses: DeterminateSystems/magic-nix-cache-action@main 25 | - uses: cachix/cachix-action@v12 26 | with: 27 | name: polyland 28 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 29 | 30 | - run: nix build -L ${{ matrix.command }} 31 | -------------------------------------------------------------------------------- /.github/workflows/nix-ci.yml: -------------------------------------------------------------------------------- 1 | name: Nix 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | wlroots: 7 | if: github.event_name != 'pull_request' 8 | uses: ./.github/workflows/nix-update-wlroots.yml 9 | secrets: inherit 10 | 11 | build: 12 | if: always() && !cancelled() && !contains(needs.*.result, 'failure') 13 | needs: wlroots 14 | uses: ./.github/workflows/nix-build.yml 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/nix-update-inputs.yml: -------------------------------------------------------------------------------- 1 | name: Nix 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # check daily 6 | 7 | jobs: 8 | update: 9 | name: inputs 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Clone repository 13 | uses: actions/checkout@v3 14 | with: 15 | token: ${{ secrets.PAT }} 16 | 17 | - uses: DeterminateSystems/nix-installer-action@main 18 | - name: Update inputs 19 | run: nix/update-inputs.sh 20 | 21 | - name: Commit 22 | uses: stefanzweifel/git-auto-commit-action@v4 23 | with: 24 | commit_message: "[gha] Nix: update inputs" 25 | 26 | update-build: 27 | needs: update 28 | uses: ./.github/workflows/nix-build.yml 29 | secrets: inherit 30 | -------------------------------------------------------------------------------- /.github/workflows/nix-update-wlroots.yml: -------------------------------------------------------------------------------- 1 | name: Nix 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | PAT: 7 | required: true 8 | 9 | jobs: 10 | update: 11 | name: wlroots 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Clone repository 15 | uses: actions/checkout@v3 16 | with: 17 | token: ${{ secrets.PAT }} 18 | 19 | - uses: DeterminateSystems/nix-installer-action@main 20 | - name: Update lockfile 21 | run: nix/update-wlroots.sh 22 | 23 | - name: Commit 24 | uses: stefanzweifel/git-auto-commit-action@v4 25 | with: 26 | commit_message: "[gha] Nix: update wlroots" 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release artifacts 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | source-tarball: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout Polyland 13 | id: checkout 14 | uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | 18 | - name: Generate version 19 | id: genversion 20 | run: | 21 | bash -c scripts/generateVersion.sh 22 | mv scripts/generateVersion.sh scripts/generateVersion.sh.bak 23 | 24 | - name: Create tarball with submodules 25 | id: tar 26 | run: | 27 | mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz * 28 | 29 | - id: whatrelease 30 | name: Get latest release 31 | uses: pozetroninc/github-action-get-latest-release@master 32 | with: 33 | owner: poly-land 34 | repo: Polyland 35 | excludes: prerelease, draft 36 | 37 | - name: Upload to release 38 | id: upload 39 | uses: svenstaro/upload-release-action@v2 40 | with: 41 | repo_token: ${{ secrets.GITHUB_TOKEN }} 42 | file: source.tar.gz 43 | asset_name: source-${{ steps.whatrelease.outputs.release }}.tar.gz 44 | tag: ${{ steps.whatrelease.outputs.release }} 45 | overwrite: true 46 | -------------------------------------------------------------------------------- /.github/workflows/security-checks.yml: -------------------------------------------------------------------------------- 1 | name: Security Checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | flawfinder: 7 | name: Flawfinder Checks 8 | runs-on: ubuntu-latest 9 | permissions: 10 | actions: read 11 | contents: read 12 | security-events: write 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v3 16 | 17 | - name: Scan with Flawfinder 18 | uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c 19 | with: 20 | arguments: '--sarif ./' 21 | output: 'flawfinder_results.sarif' 22 | 23 | - name: Upload analysis results to GitHub Security tab 24 | uses: github/codeql-action/upload-sarif@v2 25 | with: 26 | sarif_file: ${{github.workspace}}/flawfinder_results.sarif 27 | 28 | codeql: 29 | name: CodeQL 30 | runs-on: ubuntu-latest 31 | container: 32 | image: archlinux 33 | 34 | permissions: 35 | actions: read 36 | contents: read 37 | security-events: write 38 | 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | language: [ 'cpp' ] 43 | 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@v3 47 | 48 | - name: Initialize CodeQL 49 | uses: github/codeql-action/init@v2 50 | with: 51 | languages: ${{ matrix.language }} 52 | 53 | - name: Init Hyprland build 54 | run: | 55 | sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf 56 | pacman --noconfirm --noprogressbar -Syyu 57 | pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff 58 | useradd -m githubuser 59 | echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers 60 | su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar" 61 | git config --global --add safe.directory /__w/Hyprland/Hyprland 62 | 63 | - name: Checkout Hyprland 64 | uses: actions/checkout@v3 65 | with: 66 | submodules: recursive 67 | 68 | - name: Build Hyprland 69 | run: | 70 | git submodule sync --recursive && git submodule update --init --force --recursive 71 | make all 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v2 75 | with: 76 | category: "/language:${{matrix.language}}" 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | _deps 11 | 12 | build/ 13 | result* 14 | /.vscode/ 15 | /.idea/ 16 | .envrc 17 | .cache 18 | 19 | *.o 20 | *-protocol.c 21 | *-protocol.h 22 | .ccls-cache 23 | *.so 24 | 25 | hyprctl/hyprctl 26 | 27 | gmon.out 28 | *.out 29 | *.tar.gz 30 | 31 | PKGBUILD 32 | 33 | src/version.h 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wlroots"] 2 | path = subprojects/wlroots 3 | url = https://gitlab.freedesktop.org/wlroots/wlroots.git 4 | [submodule "subprojects/hyprland-protocols"] 5 | path = subprojects/hyprland-protocols 6 | url = https://github.com/hyprwm/hyprland-protocols 7 | [submodule "subprojects/udis86"] 8 | path = subprojects/udis86 9 | url = https://github.com/canihavesomecoffee/udis86 10 | [submodule "subprojects/tracy"] 11 | path = subprojects/tracy 12 | url = https://github.com/wolfpld/tracy 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Goal 2 | 3 | Our goal is to provide a space where it is safe for everyone to contribute to, 4 | and get support for, open-source software in a respectful and cooperative 5 | manner. 6 | 7 | We value all contributions and want to make this organization and its 8 | surrounding community a place for everyone. 9 | 10 | As members, contributors, and everyone else who may participate in the 11 | development, we strive to keep the entire experience civil. 12 | 13 | ## Standards 14 | 15 | Our community standards exist in order to make sure everyone feels comfortable 16 | contributing to the project(s) together. 17 | 18 | Our standards are: 19 | - Do not harass, attack, or in any other way discriminate against anyone, including 20 | for their protected traits, including, but not limited to, sex, religion, race, 21 | appearance, gender, identity, nationality, sexuality, etc. 22 | - Do not go off-topic, do not post spam. 23 | - Treat everyone with respect. 24 | 25 | Examples of breaking each rule respectively include: 26 | - Harassment, bullying or inappropriate jokes about another person. 27 | - Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand. 28 | - Treating someone as worse because of their lack of understanding of an issue. 29 | 30 | ## Enforcement 31 | 32 | Enforcement of this CoC is done by the members of the hyprwm organization. 33 | 34 | We, as the organization, will strive our best to keep this community civil and 35 | following the standards outlined above. 36 | 37 | ### Reporting incidents 38 | 39 | If you believe an incident of breaking our standards has occurred, but nobody has 40 | taken appropriate action, you can privately contact the people responsible for dealing 41 | with such incidents in multiple ways: 42 | 43 | ***E-Mail*** 44 | - `vaxry[at]vaxry.net` 45 | - `mihai[at]fufexan.net` 46 | 47 | ***Discord*** 48 | - `@vaxry` 49 | - `@fufexan` 50 | 51 | ***Matrix*** 52 | - `@vaxry:matrix.vaxry.net` 53 | - `@fufexan:matrix.org` 54 | 55 | We, as members, guarantee your privacy and will not share those reports with anyone. 56 | 57 | ## Enforcement Strategy 58 | 59 | Depending on the severity of the infraction, any action from the list below may be applied. 60 | Please keep in mind cases are reviewed on a per-case basis and members are the ultimate 61 | deciding factor in the type of punishment. 62 | 63 | If the matter would benefit from an outside opinion, a member might reach for more opinions 64 | from people unrelated to the organization, however, the final decision regarding the action 65 | to be taken is still up to the member. 66 | 67 | For example, if the matter at hand regards a representative of a marginalized group or minority, 68 | the member might ask for a first-hand opinion from another representative of such group. 69 | 70 | ### Correction/Edit 71 | 72 | If your message is found to be misleading or poorly worded, a member might 73 | edit your message. 74 | 75 | ### Warning/Deletion 76 | 77 | If your message is found inappropriate, a member might give you a public or private warning, 78 | and/or delete your message. 79 | 80 | ### Mute 81 | 82 | If your message is disruptive, or you have been repeatedly violating the standards, 83 | a member might mute (or temporarily ban) you. 84 | 85 | ### Ban 86 | 87 | If your message is hateful, very disruptive, or other, less serious infractions are repeated 88 | ignoring previous punishments, a member might ban you permanently. 89 | 90 | ## Scope 91 | 92 | This CoC shall apply to all projects ran under the `hyprwm` organization and all _official_ communities 93 | outside of GitHub. 94 | 95 | However, it is worth noting that official communities outside of GitHub might have their own, 96 | additional sets of rules. 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022-2023, vaxerski 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /assets/hyprland-portals.conf: -------------------------------------------------------------------------------- 1 | [preferred] 2 | default=hyprland;gtk -------------------------------------------------------------------------------- /assets/hyprland.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/hyprland.png -------------------------------------------------------------------------------- /assets/meson.build: -------------------------------------------------------------------------------- 1 | wallpaper_types = ['', 'anime_', 'anime2_'] 2 | 3 | foreach type : wallpaper_types 4 | foreach size : [2, 4, 8] 5 | install_data(f'wall_@type@@size@K.png', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') 6 | endforeach 7 | endforeach 8 | 9 | install_data('hyprland-portals.conf', install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'), install_tag: 'runtime') 10 | -------------------------------------------------------------------------------- /assets/wall_2K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_2K.png -------------------------------------------------------------------------------- /assets/wall_4K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_4K.png -------------------------------------------------------------------------------- /assets/wall_8K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_8K.png -------------------------------------------------------------------------------- /assets/wall_anime2_2K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime2_2K.png -------------------------------------------------------------------------------- /assets/wall_anime2_4K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime2_4K.png -------------------------------------------------------------------------------- /assets/wall_anime2_8K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime2_8K.png -------------------------------------------------------------------------------- /assets/wall_anime_2K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime_2K.png -------------------------------------------------------------------------------- /assets/wall_anime_4K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime_4K.png -------------------------------------------------------------------------------- /assets/wall_anime_8K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/poly-land/Polyland/f757a1023a5248559a13de053f12e911a98a2ebc/assets/wall_anime_8K.png -------------------------------------------------------------------------------- /docs/Hyprland.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "Hyprland" "1" "" "" "Hyprland User Manual" 4 | .hy 5 | .SH NAME 6 | .PP 7 | Hyprland - Dynamic tiling Wayland compositor 8 | .SH SYNOPSIS 9 | .PP 10 | \f[B]Hyprland\f[R] [\f[I]arg [...]\f[R]]. 11 | .SH DESCRIPTION 12 | .PP 13 | \f[B]Hyprland\f[R] is a dynamic tiling Wayland compositor based on 14 | wlroots that doesn\[aq]t sacrifice on its looks. 15 | .PP 16 | You can launch Hyprland by either going into a TTY and executing 17 | \f[B]Hyprland\f[R], or with a login manager. 18 | .SH NOTICE 19 | .PP 20 | Hyprland is still in pretty early development compared to some other 21 | Wayland compositors. 22 | .PP 23 | Although Hyprland is pretty stable, it may have some bugs. 24 | .SH CONFIGURATION 25 | .PP 26 | For configuration information please see 27 | <\f[I]https://github.com/hyprwm/Hyprland/wiki\f[R]>. 28 | .SH OPTIONS 29 | .TP 30 | \f[B]-h\f[R], \f[B]--help\f[R] 31 | Show command usage. 32 | .TP 33 | \f[B]-c\f[R], \f[B]--config\f[R] 34 | Specify config file to use. 35 | .SH BUGS 36 | .TP 37 | Submit bug reports and request features online at: 38 | <\f[I]https://github.com/hyprwm/Hyprland/issues\f[R]> 39 | .SH SEE ALSO 40 | .PP 41 | Sources at: <\f[I]https://github.com/hyprwm/Hyprland\f[R]> 42 | .SH COPYRIGHT 43 | .PP 44 | Copyright (c) 2022, vaxerski 45 | .SH AUTHORS 46 | Vaxerski <\f[I]https://github.com/vaxerski\f[R]>. 47 | -------------------------------------------------------------------------------- /docs/Hyprland.1.rst: -------------------------------------------------------------------------------- 1 | :title: Hyprland 2 | :author: Vaxerski <*https://github.com/vaxerski*> 3 | 4 | NAME 5 | ==== 6 | 7 | Hyprland - Dynamic tiling Wayland compositor 8 | 9 | SYNOPSIS 10 | ======== 11 | 12 | **Hyprland** [*arg [...]*]. 13 | 14 | DESCRIPTION 15 | =========== 16 | 17 | **Hyprland** is a dynamic tiling Wayland compositor based on 18 | wlroots that doesn't sacrifice on its looks. 19 | 20 | You can launch Hyprland by either going into a TTY and 21 | executing **Hyprland**, or with a login manager. 22 | 23 | NOTICE 24 | ====== 25 | 26 | Hyprland is still in pretty early development compared to some other Wayland compositors. 27 | 28 | Although Hyprland is pretty stable, it may have some bugs. 29 | 30 | CONFIGURATION 31 | ============= 32 | 33 | For configuration information please see <*https://github.com/hyprwm/Hyprland/wiki*>. 34 | 35 | OPTIONS 36 | ======= 37 | 38 | **-h**, **--help** 39 | Show command usage. 40 | 41 | **-c**, **--config** 42 | Specify config file to use. 43 | 44 | BUGS 45 | ==== 46 | 47 | Submit bug reports and request features online at: 48 | <*https://github.com/hyprwm/Hyprland/issues*> 49 | 50 | SEE ALSO 51 | ======== 52 | 53 | Sources at: <*https://github.com/hyprwm/Hyprland*> 54 | 55 | COPYRIGHT 56 | ========= 57 | 58 | Copyright (c) 2022, vaxerski 59 | -------------------------------------------------------------------------------- /docs/ISSUE_GUIDELINES.md: -------------------------------------------------------------------------------- 1 | # Issue Guidelines 2 | 3 | First of all, please remember to: 4 | - Check that your issue is not a duplicate 5 | - Read the [FAQ](https://wiki.hyprland.org/FAQ/) 6 | - Read the [Configuring Page](https://wiki.hyprland.org/Configuring/Configuring-Hyprland) 7 | 8 |
9 | 10 | # Reporting suggestions 11 | Suggestions are welcome. 12 | 13 | Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hyprland.org/IPC). Please do not suggest features that can be implemented as such. 14 | 15 |
16 | 17 | # Reporting bugs 18 | 19 | All bug reports should have the following: 20 | - Steps to reproduce 21 | - Expected outcome 22 | - Noted outcome 23 | 24 | If your bug is one that doesn't crash Hyprland, but feels like invalid behavior, that's all you need to say. 25 | 26 | If your bug crashes Hyprland, append additionally: 27 | - The Hyprland log 28 | - Your config 29 | - (v0.22.0beta and up) The Hyprland Crash Report 30 | - (v0.21.0beta and below) Coredump / Coredump analysis (with a stacktrace) 31 | 32 | **Important**: Please do NOT use any package for reporting bugs! Clone and compile from source. 33 | 34 | ## Obtaining the Hyprland log 35 | If you are in a TTY, and the hyprland session that crashed was the last one you launched, the log will be printed with 36 | ``` 37 | cat /tmp/hypr/$(ls -t /tmp/hypr/ | head -n 1)/hyprland.log 38 | ``` 39 | feel free to send it to a file, save, copy, etc. 40 | 41 | if you are in a Hyprland session, and you want the log of the last session, use 42 | ``` 43 | cat /tmp/hypr/$(ls -t /tmp/hypr/ | head -n 2 | tail -n 1)/hyprland.log 44 | ``` 45 | 46 | basically, directories in /tmp/hypr are your sessions. 47 | 48 | ## Obtaining the Hyprland Crash Report (v0.22.0beta and up) 49 | 50 | If you have `$XDG_CACHE_HOME` set, the crash report directory is `$XDG_CACHE_HOME/hyprland`. If not, it's `~/.hyprland` 51 | 52 | Go to the crash report directory and you should find a file named `hyprlandCrashReport[XXXX].txt` where `[XXXX]` is the PID of the process that crashed. 53 | 54 | Attach that file to your issue. 55 | ## Obtaining the Hyprland coredump (v0.21.0beta and below) 56 | If you are on systemd, you can simply use 57 | ``` 58 | coredumpctl 59 | ``` 60 | then go to the end (press END on your keyboard) and remember the PID of the last `Hyprland` occurrence. It's the first number after the time, for example `2891`. 61 | 62 | exit coredumpctl (ctrl+c) and use 63 | ``` 64 | coredumpctl info [PID] 65 | ``` 66 | where `[PID]` is the PID you remembered. 67 | 68 | ## Obtaining the debug Hyprland coredump 69 | A debug coredump provides more information for debugging and may speed up the process of fixing the bug. 70 | 71 | Make sure you're on latest git. Run `git pull --recurse-submodules` to sync everything. 72 | 73 | 1. [Compile Hyprland with debug mode](http://wiki.hyprland.org/Contributing-and-Debugging/#build-in-debug-mode) 74 | > Note: The config file used will be `hyprlandd.conf` instead of `hyprland.conf` 75 | 76 | 2. `cd ~` 77 | 3. For your own convenience, launch Hyprland from a tty with the envvar `ASAN_OPTIONS="log_path=asan.log" ~/path/to/Hyprland` 78 | 4. Reproduce the crash. Hyprland should instantly close. 79 | 5. Check out your `~` and find a file called `asan.log.XXXXX` where `XXXXX` will be a number corresponding to the PID of the Hyprland instance that crashed. 80 | 6. That is your coredump. Attach it to your issue. 81 | -------------------------------------------------------------------------------- /docs/hyprctl.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "hyprctl" "1" "" "" "hyprctl User Manual" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hyprctl - Utility for controlling parts of Hyprland from a CLI or a 8 | script 9 | .SH SYNOPSIS 10 | .PP 11 | \f[B]hyprctl\f[R] [\f[I](opt)flags\f[R]] [\f[B]command\f[R]] 12 | [\f[I](opt)args\f[R]] 13 | .SH DESCRIPTION 14 | .PP 15 | \f[B]hyprctl\f[R] is a utility for controlling some parts of the 16 | compositor from a CLI or a script. 17 | .SH CONTROL COMMANDS 18 | .PP 19 | \f[B]dispatch\f[R] 20 | .RS 21 | .PP 22 | Call a dispatcher with an argument. 23 | .PP 24 | An argument must be present. 25 | For dispatchers without parameters it can be anything. 26 | .PP 27 | Returns: \f[I]ok\f[R] on success, and an error message on failure. 28 | .TP 29 | Examples: 30 | \f[B]hyprctl\f[R] \f[I]dispatch exec kitty\f[R] 31 | .RS 32 | .PP 33 | \f[B]hyprctl\f[R] \f[I]dispatch pseudo x\f[R] 34 | .RE 35 | .RE 36 | .PP 37 | \f[B]keyword\f[R] 38 | .RS 39 | .PP 40 | Set a config keyword dynamically. 41 | .PP 42 | Returns: \f[I]ok\f[R] on success, and an error message on failure. 43 | .TP 44 | Examples: 45 | \f[B]hyprctl\f[R] \f[I]keyword bind SUPER,0,pseudo\f[R] 46 | .RS 47 | .PP 48 | \f[B]hyprctl\f[R] \f[I]keyword general:border_size 10\f[R] 49 | .RE 50 | .RE 51 | .PP 52 | \f[B]reload\f[R] 53 | .RS 54 | .PP 55 | Force a reload of the config file. 56 | .RE 57 | .PP 58 | \f[B]kill\f[R] 59 | .RS 60 | .PP 61 | Enter kill mode, where you can kill an app by clicking on it. 62 | You can exit by pressing ESCAPE. 63 | .RE 64 | .SH INFO COMMANDS 65 | .PP 66 | \f[B]version\f[R] 67 | .RS 68 | .PP 69 | Prints the Hyprland version, flags, commit and branch of build. 70 | .RE 71 | .PP 72 | \f[B]monitors\f[R] 73 | .RS 74 | .PP 75 | Lists all the outputs with their properties. 76 | .RE 77 | .PP 78 | \f[B]workspaces\f[R] 79 | .RS 80 | .PP 81 | Lists all workspaces with their properties. 82 | .RE 83 | .PP 84 | \f[B]clients\f[R] 85 | .RS 86 | .PP 87 | Lists all windows with their properties. 88 | .RE 89 | .PP 90 | \f[B]devices\f[R] 91 | .RS 92 | .PP 93 | Lists all connected input devices. 94 | .RE 95 | .PP 96 | \f[B]activewindow\f[R] 97 | .RS 98 | .PP 99 | Returns the active window name. 100 | .RE 101 | .PP 102 | \f[B]layers\f[R] 103 | .RS 104 | .PP 105 | Lists all the layers. 106 | .RE 107 | .PP 108 | \f[B]splash\f[R] 109 | .RS 110 | .PP 111 | Returns the current random splash. 112 | .RE 113 | .SH OPTIONS 114 | .PP 115 | \f[B]--batch\f[R] 116 | .RS 117 | .PP 118 | Specify a batch of commands to execute. 119 | .TP 120 | Example: 121 | \f[B]hyprctl\f[R] \f[I]--batch \[dq]keyword general:border_size 2 ; 122 | keyword general:gaps_out 20\[dq]\f[R] 123 | .RS 124 | .PP 125 | \f[I];\f[R] separates the commands. 126 | .RE 127 | .RE 128 | .PP 129 | \f[B]-j\f[R] 130 | .RS 131 | .PP 132 | Outputs information in JSON. 133 | .RE 134 | .SH BUGS 135 | .TP 136 | Submit bug reports and request features online at: 137 | <\f[I]https://github.com/hyprwm/Hyprland/issues\f[R]> 138 | .SH SEE ALSO 139 | .PP 140 | Sources at: <\f[I]https://github.com/hyprwm/Hyprland\f[R]> 141 | .SH COPYRIGHT 142 | .PP 143 | Copyright (c) 2022, vaxerski 144 | .SH AUTHORS 145 | Vaxerski <\f[I]https://github.com/vaxerski\f[R]>. 146 | -------------------------------------------------------------------------------- /docs/hyprctl.1.rst: -------------------------------------------------------------------------------- 1 | :title: hyprctl(1) 2 | :author: Vaxerski <*https://github.com/vaxerski*> 3 | 4 | NAME 5 | ==== 6 | 7 | hyprctl - Utility for controlling parts of Hyprland from a CLI or a script 8 | 9 | SYNOPSIS 10 | ======== 11 | 12 | **hyprctl** [*(opt)flags*] [**command**] [*(opt)args*] 13 | 14 | DESCRIPTION 15 | =========== 16 | 17 | **hyprctl** is a utility for controlling some parts of the compositor from a CLI or a script. 18 | 19 | CONTROL COMMANDS 20 | ================ 21 | 22 | **dispatch** 23 | 24 | Call a dispatcher with an argument. 25 | 26 | An argument must be present. 27 | For dispatchers without parameters it can be anything. 28 | 29 | Returns: *ok* on success, and an error message on failure. 30 | 31 | Examples: 32 | **hyprctl** *dispatch exec kitty* 33 | 34 | **hyprctl** *dispatch pseudo x* 35 | 36 | **keyword** 37 | 38 | Set a config keyword dynamically. 39 | 40 | Returns: *ok* on success, and an error message on failure. 41 | 42 | Examples: 43 | **hyprctl** *keyword bind SUPER,0,pseudo* 44 | 45 | **hyprctl** *keyword general:border_size 10* 46 | 47 | **reload** 48 | 49 | Force a reload of the config file. 50 | 51 | **kill** 52 | 53 | Enter kill mode, where you can kill an app by clicking on it. 54 | You can exit by pressing ESCAPE. 55 | 56 | INFO COMMANDS 57 | ============= 58 | 59 | **version** 60 | 61 | Prints the Hyprland version, flags, commit and branch of build. 62 | 63 | **monitors** 64 | 65 | Lists all the outputs with their properties. 66 | 67 | **workspaces** 68 | 69 | Lists all workspaces with their properties. 70 | 71 | **clients** 72 | 73 | Lists all windows with their properties. 74 | 75 | **devices** 76 | 77 | Lists all connected input devices. 78 | 79 | **activewindow** 80 | 81 | Returns the active window name. 82 | 83 | **layers** 84 | 85 | Lists all the layers. 86 | 87 | **splash** 88 | 89 | Returns the current random splash. 90 | 91 | OPTIONS 92 | ======= 93 | 94 | **--batch** 95 | 96 | Specify a batch of commands to execute. 97 | 98 | Example: 99 | **hyprctl** *--batch "keyword general:border_size 2 ; keyword general:gaps_out 20"* 100 | 101 | *;* separates the commands. 102 | 103 | **-j** 104 | 105 | Outputs information in JSON. 106 | 107 | BUGS 108 | ==== 109 | 110 | Submit bug reports and request features online at: 111 | <*https://github.com/hyprwm/Hyprland/issues*> 112 | 113 | SEE ALSO 114 | ======== 115 | 116 | Sources at: <*https://github.com/hyprwm/Hyprland*> 117 | 118 | COPYRIGHT 119 | ========= 120 | 121 | Copyright (c) 2022, vaxerski 122 | -------------------------------------------------------------------------------- /docs/meson.build: -------------------------------------------------------------------------------- 1 | install_man ('Hyprland.1') 2 | install_man ('hyprctl.1') 3 | -------------------------------------------------------------------------------- /example/examplePlugin/Makefile: -------------------------------------------------------------------------------- 1 | # compile with HYPRLAND_HEADERS= make all 2 | # make sure that the path above is to the root hl repo directory, NOT src/ 3 | # and that you have ran `make protocols` in the hl dir. 4 | 5 | all: 6 | $(CXX) -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b 7 | clean: 8 | rm ./examplePlugin.so 9 | -------------------------------------------------------------------------------- /example/examplePlugin/customDecoration.cpp: -------------------------------------------------------------------------------- 1 | #include "customDecoration.hpp" 2 | #include 3 | #include 4 | #include "globals.hpp" 5 | 6 | CCustomDecoration::CCustomDecoration(CWindow* pWindow) { 7 | m_pWindow = pWindow; 8 | m_vLastWindowPos = pWindow->m_vRealPosition.vec(); 9 | m_vLastWindowSize = pWindow->m_vRealSize.vec(); 10 | } 11 | 12 | CCustomDecoration::~CCustomDecoration() { 13 | damageEntire(); 14 | } 15 | 16 | SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() { 17 | return m_seExtents; 18 | } 19 | 20 | void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) { 21 | if (!g_pCompositor->windowValidMapped(m_pWindow)) 22 | return; 23 | 24 | if (!m_pWindow->m_sSpecialRenderData.decorate) 25 | return; 26 | 27 | static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue; 28 | static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue; 29 | static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue; 30 | 31 | const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ? 32 | 0 : 33 | (m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying()); 34 | 35 | // draw the border 36 | CBox fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE), 37 | (int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)}; 38 | 39 | fullBox.x -= pMonitor->vecPosition.x; 40 | fullBox.y -= pMonitor->vecPosition.y; 41 | 42 | m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2}, 43 | {fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2, 44 | fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}}; 45 | 46 | fullBox.x += offset.x; 47 | fullBox.y += offset.y; 48 | 49 | if (fullBox.width < 1 || fullBox.height < 1) 50 | return; // don't draw invisible shadows 51 | 52 | g_pHyprOpenGL->scissor((CBox*)nullptr); 53 | 54 | fullBox.scale(pMonitor->scale); 55 | g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a); 56 | } 57 | 58 | eDecorationType CCustomDecoration::getDecorationType() { 59 | return DECORATION_CUSTOM; 60 | } 61 | 62 | void CCustomDecoration::updateWindow(CWindow* pWindow) { 63 | 64 | m_vLastWindowPos = pWindow->m_vRealPosition.vec(); 65 | m_vLastWindowSize = pWindow->m_vRealSize.vec(); 66 | 67 | damageEntire(); 68 | } 69 | 70 | void CCustomDecoration::damageEntire() { 71 | CBox dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y), 72 | (int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y}; 73 | g_pHyprRenderer->damageBox(&dm); 74 | } -------------------------------------------------------------------------------- /example/examplePlugin/customDecoration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WLR_USE_UNSTABLE 4 | 5 | #include 6 | 7 | class CCustomDecoration : public IHyprWindowDecoration { 8 | public: 9 | CCustomDecoration(CWindow*); 10 | virtual ~CCustomDecoration(); 11 | 12 | virtual SWindowDecorationExtents getWindowDecorationExtents(); 13 | 14 | virtual void draw(CMonitor*, float a, const Vector2D& offset); 15 | 16 | virtual eDecorationType getDecorationType(); 17 | 18 | virtual void updateWindow(CWindow*); 19 | 20 | virtual void damageEntire(); 21 | 22 | private: 23 | SWindowDecorationExtents m_seExtents; 24 | 25 | CWindow* m_pWindow = nullptr; 26 | 27 | Vector2D m_vLastWindowPos; 28 | Vector2D m_vLastWindowSize; 29 | }; -------------------------------------------------------------------------------- /example/examplePlugin/customLayout.cpp: -------------------------------------------------------------------------------- 1 | #include "customLayout.hpp" 2 | #include 3 | #include "globals.hpp" 4 | 5 | void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) { 6 | const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); 7 | const auto SIZE = PMONITOR->vecSize; 8 | 9 | // these are used for focus and move calculations, and are *required* to touch for moving focus to work properly. 10 | pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)}; 11 | pWindow->m_vSize = SIZE / 2.0; 12 | 13 | // this is the actual pos and size of the window (where it's rendered) 14 | pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10}; 15 | pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20}; 16 | 17 | const auto PDATA = &m_vWindowData.emplace_back(); 18 | PDATA->pWindow = pWindow; 19 | } 20 | 21 | void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) { 22 | std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; }); 23 | } 24 | 25 | bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) { 26 | return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end(); 27 | } 28 | 29 | void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) { 30 | ; // empty 31 | } 32 | 33 | void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) { 34 | ; // empty 35 | } 36 | 37 | void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) { 38 | ; // empty 39 | } 40 | 41 | void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) { 42 | ; // empty 43 | } 44 | 45 | std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) { 46 | return ""; 47 | } 48 | 49 | SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) { 50 | return {}; 51 | } 52 | 53 | void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) { 54 | ; // empty 55 | } 56 | 57 | void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) { 58 | ; // empty 59 | } 60 | 61 | std::string CHyprCustomLayout::getLayoutName() { 62 | return "custom"; 63 | } 64 | 65 | void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) { 66 | ; // empty 67 | } 68 | 69 | void CHyprCustomLayout::onEnable() { 70 | for (auto& w : g_pCompositor->m_vWindows) { 71 | if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating) 72 | continue; 73 | 74 | onWindowCreatedTiling(w.get()); 75 | } 76 | } 77 | 78 | void CHyprCustomLayout::onDisable() { 79 | m_vWindowData.clear(); 80 | } -------------------------------------------------------------------------------- /example/examplePlugin/customLayout.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WLR_USE_UNSTABLE 4 | 5 | #include 6 | 7 | struct SWindowData { 8 | CWindow* pWindow = nullptr; 9 | }; 10 | 11 | class CHyprCustomLayout : public IHyprLayout { 12 | public: 13 | virtual void onWindowCreatedTiling(CWindow*); 14 | virtual void onWindowRemovedTiling(CWindow*); 15 | virtual bool isWindowTiled(CWindow*); 16 | virtual void recalculateMonitor(const int&); 17 | virtual void recalculateWindow(CWindow*); 18 | virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr); 19 | virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool); 20 | virtual std::any layoutMessage(SLayoutMessageHeader, std::string); 21 | virtual SWindowRenderLayoutHints requestRenderHints(CWindow*); 22 | virtual void switchWindows(CWindow*, CWindow*); 23 | virtual void alterSplitRatio(CWindow*, float, bool); 24 | virtual std::string getLayoutName(); 25 | virtual void replaceWindowDataWith(CWindow* from, CWindow* to); 26 | 27 | virtual void onEnable(); 28 | virtual void onDisable(); 29 | 30 | private: 31 | std::vector m_vWindowData; 32 | }; -------------------------------------------------------------------------------- /example/examplePlugin/globals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | inline HANDLE PHANDLE = nullptr; -------------------------------------------------------------------------------- /example/hyprland.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Hyprland 3 | Comment=An intelligent dynamic tiling Wayland compositor 4 | Exec=Hyprland 5 | Type=Application -------------------------------------------------------------------------------- /example/hyprland.service: -------------------------------------------------------------------------------- 1 | ; a primitive systemd --user example 2 | [Unit] 3 | Description = %p 4 | BindsTo = graphical-session.target 5 | Upholds = swaybg@333333.service 6 | 7 | [Service] 8 | Type = notify 9 | ExecStart = /usr/bin/Hyprland 10 | 11 | [Install] 12 | WantedBy = default.target 13 | -------------------------------------------------------------------------------- /example/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/Hyprland", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${fileDirname}", 15 | "externalConsole": false, 16 | "MIMode": "gdb", 17 | "setupCommands": [ 18 | { 19 | "description": "Enable pretty-printing for gdb", 20 | "text": "-enable-pretty-printing", 21 | "ignoreFailures": true 22 | } 23 | ] 24 | }, 25 | ] 26 | } -------------------------------------------------------------------------------- /example/meson.build: -------------------------------------------------------------------------------- 1 | install_data('hyprland.conf', install_dir: join_paths(get_option('datadir'), 'hyprland'), install_tag: 'runtime') 2 | install_data('hyprland.desktop', install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), install_tag: 'runtime') 3 | -------------------------------------------------------------------------------- /example/screenShader.frag: -------------------------------------------------------------------------------- 1 | // 2 | // Example blue light filter shader. 3 | // 4 | 5 | precision mediump float; 6 | varying vec2 v_texcoord; 7 | uniform sampler2D tex; 8 | 9 | void main() { 10 | 11 | vec4 pixColor = texture2D(tex, v_texcoord); 12 | 13 | pixColor[2] *= 0.8; 14 | 15 | gl_FragColor = pixColor; 16 | } 17 | -------------------------------------------------------------------------------- /example/swaybg@.service: -------------------------------------------------------------------------------- 1 | ; a primitive systemd --user example 2 | ; see example/hyprland.service for more details 3 | [Unit] 4 | Description = %p 5 | BindsTo = hyprland.service 6 | Wants = hyprland.service 7 | After = hyprland.service 8 | 9 | [Service] 10 | ExecStart = /usr/bin/swaybg --color #%i 11 | 12 | [Install] 13 | WantedBy = default.target 14 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "hyprland-protocols": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ], 8 | "systems": [ 9 | "systems" 10 | ] 11 | }, 12 | "locked": { 13 | "lastModified": 1691753796, 14 | "narHash": "sha256-zOEwiWoXk3j3+EoF3ySUJmberFewWlagvewDRuWYAso=", 15 | "owner": "hyprwm", 16 | "repo": "hyprland-protocols", 17 | "rev": "0c2ce70625cb30aef199cb388f99e19a61a6ce03", 18 | "type": "github" 19 | }, 20 | "original": { 21 | "owner": "hyprwm", 22 | "repo": "hyprland-protocols", 23 | "type": "github" 24 | } 25 | }, 26 | "hyprlang": { 27 | "inputs": { 28 | "nixpkgs": [ 29 | "xdph", 30 | "nixpkgs" 31 | ] 32 | }, 33 | "locked": { 34 | "lastModified": 1704287638, 35 | "narHash": "sha256-TuRXJGwtK440AXQNl5eiqmQqY4LZ/9+z/R7xC0ie3iA=", 36 | "owner": "hyprwm", 37 | "repo": "hyprlang", 38 | "rev": "6624f2bb66d4d27975766e81f77174adbe58ec97", 39 | "type": "github" 40 | }, 41 | "original": { 42 | "owner": "hyprwm", 43 | "repo": "hyprlang", 44 | "type": "github" 45 | } 46 | }, 47 | "nixpkgs": { 48 | "locked": { 49 | "lastModified": 1707546158, 50 | "narHash": "sha256-nYYJTpzfPMDxI8mzhQsYjIUX+grorqjKEU9Np6Xwy/0=", 51 | "owner": "NixOS", 52 | "repo": "nixpkgs", 53 | "rev": "d934204a0f8d9198e1e4515dd6fec76a139c87f0", 54 | "type": "github" 55 | }, 56 | "original": { 57 | "owner": "NixOS", 58 | "ref": "nixos-unstable", 59 | "repo": "nixpkgs", 60 | "type": "github" 61 | } 62 | }, 63 | "root": { 64 | "inputs": { 65 | "hyprland-protocols": "hyprland-protocols", 66 | "nixpkgs": "nixpkgs", 67 | "systems": "systems", 68 | "wlroots": "wlroots", 69 | "xdph": "xdph" 70 | } 71 | }, 72 | "systems": { 73 | "locked": { 74 | "lastModified": 1689347949, 75 | "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", 76 | "owner": "nix-systems", 77 | "repo": "default-linux", 78 | "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", 79 | "type": "github" 80 | }, 81 | "original": { 82 | "owner": "nix-systems", 83 | "repo": "default-linux", 84 | "type": "github" 85 | } 86 | }, 87 | "wlroots": { 88 | "flake": false, 89 | "locked": { 90 | "host": "gitlab.freedesktop.org", 91 | "lastModified": 1697909146, 92 | "narHash": "sha256-jU0I6FoCKnj4zIBL4daosFWh81U1fM719Z6cae8PxSY=", 93 | "owner": "wlroots", 94 | "repo": "wlroots", 95 | "rev": "47bf87ade2bd32395615a385ebde1fefbcdf79a2", 96 | "type": "gitlab" 97 | }, 98 | "original": { 99 | "host": "gitlab.freedesktop.org", 100 | "owner": "wlroots", 101 | "repo": "wlroots", 102 | "rev": "47bf87ade2bd32395615a385ebde1fefbcdf79a2", 103 | "type": "gitlab" 104 | } 105 | }, 106 | "xdph": { 107 | "inputs": { 108 | "hyprland-protocols": [ 109 | "hyprland-protocols" 110 | ], 111 | "hyprlang": "hyprlang", 112 | "nixpkgs": [ 113 | "nixpkgs" 114 | ], 115 | "systems": [ 116 | "systems" 117 | ] 118 | }, 119 | "locked": { 120 | "lastModified": 1706521509, 121 | "narHash": "sha256-AInZ50acOJ3wzUwGzNr1TmxGTMx+8j6oSTzz4E7Vbp8=", 122 | "owner": "hyprwm", 123 | "repo": "xdg-desktop-portal-hyprland", 124 | "rev": "c06fd88b3da492b8f9067be021b9184f7012b5a8", 125 | "type": "github" 126 | }, 127 | "original": { 128 | "owner": "hyprwm", 129 | "repo": "xdg-desktop-portal-hyprland", 130 | "type": "github" 131 | } 132 | } 133 | }, 134 | "root": "root", 135 | "version": 7 136 | } 137 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Polyland is a queer-friendly dynamic tiling Wayland compositor that doesn't sacrifice on its looks"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | 7 | # 8 | systems.url = "github:nix-systems/default-linux"; 9 | 10 | wlroots = { 11 | type = "gitlab"; 12 | host = "gitlab.freedesktop.org"; 13 | owner = "wlroots"; 14 | repo = "wlroots"; 15 | rev = "47bf87ade2bd32395615a385ebde1fefbcdf79a2"; 16 | flake = false; 17 | }; 18 | 19 | hyprland-protocols = { 20 | url = "github:hyprwm/hyprland-protocols"; 21 | inputs.nixpkgs.follows = "nixpkgs"; 22 | inputs.systems.follows = "systems"; 23 | }; 24 | 25 | xdph = { 26 | url = "github:hyprwm/xdg-desktop-portal-hyprland"; 27 | inputs.nixpkgs.follows = "nixpkgs"; 28 | inputs.systems.follows = "systems"; 29 | inputs.hyprland-protocols.follows = "hyprland-protocols"; 30 | }; 31 | }; 32 | 33 | outputs = inputs @ { 34 | self, 35 | nixpkgs, 36 | systems, 37 | ... 38 | }: let 39 | inherit (nixpkgs) lib; 40 | eachSystem = lib.genAttrs (import systems); 41 | pkgsFor = eachSystem (system: 42 | import nixpkgs { 43 | localSystem = system; 44 | overlays = with self.overlays; [ 45 | hyprland-packages 46 | hyprland-extras 47 | ]; 48 | }); 49 | in { 50 | overlays = import ./nix/overlays.nix {inherit self lib inputs;}; 51 | 52 | checks = eachSystem (system: 53 | (lib.filterAttrs 54 | (n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)) 55 | self.packages.${system}) 56 | // { 57 | inherit (self.packages.${system}) xdg-desktop-portal-hyprland; 58 | }); 59 | 60 | packages = eachSystem (system: { 61 | default = self.packages.${system}.hyprland; 62 | inherit 63 | (pkgsFor.${system}) 64 | # hyprland-packages 65 | hyprland 66 | hyprland-unwrapped 67 | hyprland-debug 68 | hyprland-nvidia 69 | # hyprland-extras 70 | xdg-desktop-portal-hyprland 71 | # dependencies 72 | hyprland-protocols 73 | wlroots-hyprland 74 | udis86 75 | ; 76 | }); 77 | 78 | devShells = eachSystem (system: { 79 | default = pkgsFor.${system}.mkShell.override { 80 | stdenv = pkgsFor.${system}.gcc13Stdenv; 81 | } { 82 | name = "hyprland-shell"; 83 | nativeBuildInputs = with pkgsFor.${system}; [cmake python3]; 84 | buildInputs = [self.packages.${system}.wlroots-hyprland]; 85 | inputsFrom = [ 86 | self.packages.${system}.wlroots-hyprland 87 | self.packages.${system}.hyprland 88 | ]; 89 | }; 90 | }); 91 | 92 | formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); 93 | 94 | nixosModules.default = import ./nix/module.nix inputs; 95 | homeManagerModules.default = import ./nix/hm-module.nix self; 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /hyprctl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | project( 4 | hyprctl 5 | DESCRIPTION "Control utility for Hyprland" 6 | ) 7 | 8 | add_executable(hyprctl "main.cpp") -------------------------------------------------------------------------------- /hyprctl/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(CXX) -std=c++2b ./main.cpp -o ./hyprctl 3 | clean: 4 | rm ./hyprctl 5 | -------------------------------------------------------------------------------- /hyprctl/meson.build: -------------------------------------------------------------------------------- 1 | executable('hyprctl', 'main.cpp', 2 | install: true 3 | ) 4 | -------------------------------------------------------------------------------- /hyprland.pc.in: -------------------------------------------------------------------------------- 1 | prefix="@PREFIX@" 2 | includedir="${prefix}/include" 3 | 4 | Name: Hyprland 5 | URL: https://github.com/hyprwm/Hyprland 6 | Description: Hyprland header files 7 | Version: @HYPRLAND_VERSION@ 8 | Cflags: -I"${includedir}/hyprland/protocols" -I"${includedir}/hyprland/wlroots" -I"${includedir}" 9 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('Hyprland', 'cpp', 'c', 2 | version : run_command('jq', '-r', '.version', join_paths(meson.source_root(), 'props.json'), check: true).stdout().strip(), 3 | default_options : [ 4 | 'warning_level=2', 5 | 'default_library=static', 6 | 'optimization=3', 7 | 'buildtype=release', 8 | 'debug=false' 9 | # 'cpp_std=c++23' # not yet supported by meson, as of version 0.63.0 10 | ]) 11 | 12 | # clang v14.0.6 uses C++2b instead of C++23, so we've gotta account for that 13 | # replace the following with a project default option once meson gets support for C++23 14 | cpp_compiler = meson.get_compiler('cpp') 15 | if cpp_compiler.has_argument('-std=c++23') 16 | add_global_arguments('-std=c++23', language: 'cpp') 17 | elif cpp_compiler.has_argument('-std=c++2b') 18 | add_global_arguments('-std=c++2b', language: 'cpp') 19 | else 20 | error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)') 21 | endif 22 | 23 | add_project_arguments( 24 | [ 25 | '-Wno-unused-parameter', 26 | '-Wno-unused-value', 27 | '-Wno-missing-field-initializers', 28 | '-Wno-narrowing', 29 | ], 30 | language: 'cpp') 31 | 32 | if cpp_compiler.check_header('execinfo.h') 33 | add_project_arguments('-DHAS_EXECINFO', language: 'cpp') 34 | endif 35 | 36 | wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2']) 37 | have_xwlr = wlroots.get_variable('features').get('xwayland') 38 | xcb_dep = dependency('xcb', required: get_option('xwayland')) 39 | 40 | cmake = import('cmake') 41 | udis = cmake.subproject('udis86') 42 | udis86 = udis.dependency('libudis86') 43 | 44 | if get_option('xwayland').enabled() and not have_xwlr 45 | error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support') 46 | endif 47 | have_xwayland = xcb_dep.found() and have_xwlr 48 | 49 | if not have_xwayland 50 | add_project_arguments('-DNO_XWAYLAND', language: 'cpp') 51 | endif 52 | 53 | backtrace_dep = cpp_compiler.find_library('execinfo', required: false) 54 | systemd_dep = dependency('libsystemd', required: get_option('systemd')) 55 | 56 | if get_option('systemd').enabled() 57 | if systemd_dep.found() 58 | add_project_arguments('-DUSES_SYSTEMD', language: 'cpp') 59 | else 60 | error('Cannot enable systemd in Hyprland: libsystemd was not found') 61 | endif 62 | endif 63 | 64 | if get_option('legacy_renderer').enabled() 65 | add_project_arguments('-DLEGACY_RENDERER', language: 'cpp') 66 | endif 67 | 68 | if get_option('buildtype') == 'debug' 69 | add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp') 70 | endif 71 | 72 | version_h = run_command('sh', '-c', 'scripts/generateVersion.sh') 73 | 74 | globber = run_command('find', 'src', '-name', '*.h*', check: true) 75 | headers = globber.stdout().strip().split('\n') 76 | foreach file : headers 77 | install_headers(file, subdir: 'hyprland', preserve_path: true) 78 | endforeach 79 | 80 | subdir('protocols') 81 | subdir('src') 82 | subdir('hyprctl') 83 | subdir('assets') 84 | subdir('example') 85 | subdir('docs') 86 | 87 | pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig') 88 | 89 | import('pkgconfig').generate( 90 | name: 'Hyprland', 91 | filebase: 'hyprland', 92 | url: 'https://github.com/hyprwm/Hyprland', 93 | description: 'Hyprland header files', 94 | install_dir: pkg_install_dir, 95 | subdirs: ['', 'hyprland/protocols', 'hyprland/wlroots'], 96 | ) 97 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') 2 | option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration') 3 | option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer') 4 | -------------------------------------------------------------------------------- /nix/module.nix: -------------------------------------------------------------------------------- 1 | inputs: { 2 | config, 3 | lib, 4 | pkgs, 5 | options, 6 | ... 7 | }: 8 | with lib; let 9 | cfg = config.programs.hyprland; 10 | inherit (pkgs.stdenv.hostPlatform) system; 11 | 12 | finalPortalPackage = cfg.portalPackage.override { 13 | hyprland = cfg.finalPackage; 14 | }; 15 | in { 16 | # disables Nixpkgs Hyprland module to avoid conflicts 17 | disabledModules = ["programs/hyprland.nix"]; 18 | 19 | options.programs.hyprland = { 20 | enable = 21 | mkEnableOption null 22 | // { 23 | description = mdDoc '' 24 | Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks. 25 | 26 | You can manually launch Hyprland by executing {command}`Hyprland` on a TTY. 27 | 28 | A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`. 29 | See for more information. 30 | ''; 31 | }; 32 | 33 | package = mkPackageOptionMD inputs.self.packages.${system} "hyprland" { }; 34 | 35 | finalPackage = mkOption { 36 | type = types.package; 37 | readOnly = true; 38 | default = cfg.package.override { 39 | enableXWayland = cfg.xwayland.enable; 40 | enableNvidiaPatches = cfg.enableNvidiaPatches; 41 | }; 42 | defaultText = 43 | literalExpression 44 | "`programs.hyprland.package` with applied configuration"; 45 | description = mdDoc '' 46 | The Hyprland package after applying configuration. 47 | ''; 48 | }; 49 | 50 | portalPackage = mkPackageOptionMD inputs.xdph.packages.${system} "xdg-desktop-portal-hyprland" {}; 51 | 52 | xwayland.enable = mkEnableOption (mdDoc "support for XWayland") // {default = true;}; 53 | 54 | enableNvidiaPatches = 55 | mkEnableOption null 56 | // { 57 | description = mdDoc "Whether to apply patches to wlroots for better Nvidia support."; 58 | }; 59 | }; 60 | 61 | config = mkIf cfg.enable { 62 | environment.systemPackages = [cfg.finalPackage]; 63 | 64 | # NixOS changed the name of this attribute between NixOS 23.05 and 65 | # 23.11 66 | fonts = if builtins.hasAttr "enableDefaultPackages" options.fonts 67 | then {enableDefaultPackages = mkDefault true;} 68 | else {enableDefaultFonts = mkDefault true;}; 69 | 70 | hardware.opengl.enable = mkDefault true; 71 | 72 | programs = { 73 | dconf.enable = mkDefault true; 74 | xwayland.enable = mkDefault cfg.xwayland.enable; 75 | }; 76 | 77 | security.polkit.enable = true; 78 | 79 | services.xserver.displayManager.sessionPackages = [cfg.finalPackage]; 80 | 81 | xdg.portal = { 82 | enable = mkDefault true; 83 | extraPortals = [finalPortalPackage]; 84 | }; 85 | }; 86 | 87 | imports = with lib; [ 88 | ( 89 | mkRemovedOptionModule 90 | ["programs" "hyprland" "xwayland" "hidpi"] 91 | "XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland" 92 | ) 93 | ( 94 | mkRenamedOptionModule 95 | ["programs" "hyprland" "nvidiaPatches"] 96 | ["programs" "hyprland" "enableNvidiaPatches"] 97 | ) 98 | ]; 99 | } 100 | -------------------------------------------------------------------------------- /nix/overlays.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | lib, 4 | inputs, 5 | }: let 6 | props = builtins.fromJSON (builtins.readFile ../props.json); 7 | 8 | mkDate = longDate: (lib.concatStringsSep "-" [ 9 | (builtins.substring 0 4 longDate) 10 | (builtins.substring 4 2 longDate) 11 | (builtins.substring 6 2 longDate) 12 | ]); 13 | 14 | mkJoinedOverlays = overlays: final: prev: 15 | lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays; 16 | in { 17 | # Contains what a user is most likely to care about: 18 | # Hyprland itself, XDPH and the Share Picker. 19 | default = mkJoinedOverlays (with self.overlays; [ 20 | hyprland-packages 21 | hyprland-extras 22 | ]); 23 | 24 | # Packages for variations of Hyprland, dependencies included. 25 | hyprland-packages = mkJoinedOverlays [ 26 | # Dependencies 27 | inputs.hyprland-protocols.overlays.default 28 | self.overlays.wlroots-hyprland 29 | self.overlays.udis86 30 | # Hyprland packages themselves 31 | (final: prev: { 32 | hyprland = final.callPackage ./default.nix { 33 | stdenv = final.gcc13Stdenv; 34 | version = "${props.version}+date=${mkDate (self.lastModifiedDate or "19700101")}_${self.shortRev or "dirty"}"; 35 | wlroots = final.wlroots-hyprland; 36 | commit = self.rev or ""; 37 | inherit (final) udis86 hyprland-protocols; 38 | }; 39 | hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; 40 | hyprland-debug = final.hyprland.override {debug = true;}; 41 | hyprland-nvidia = final.hyprland.override {enableNvidiaPatches = true;}; 42 | hyprland-hidpi = 43 | builtins.trace '' 44 | hyprland-hidpi was removed. Please use the hyprland package. 45 | For more information, refer to https://wiki.hyprland.org/Configuring/XWayland. 46 | '' 47 | final.hyprland; 48 | }) 49 | ]; 50 | 51 | # Packages for extra software recommended for usage with Hyprland, 52 | # including forked or patched packages for compatibility. 53 | hyprland-extras = mkJoinedOverlays [ 54 | inputs.xdph.overlays.xdg-desktop-portal-hyprland 55 | ]; 56 | 57 | udis86 = final: prev: { 58 | udis86 = final.callPackage ./udis86.nix {}; 59 | }; 60 | 61 | # Patched version of wlroots for Hyprland. 62 | # It is under a new package name so as to not conflict with 63 | # the standard version in nixpkgs. 64 | wlroots-hyprland = final: prev: { 65 | wlroots-hyprland = final.callPackage ./wlroots.nix { 66 | version = "${mkDate (inputs.wlroots.lastModifiedDate or "19700101")}_${inputs.wlroots.shortRev or "dirty"}"; 67 | src = inputs.wlroots; 68 | 69 | libdisplay-info = prev.libdisplay-info.overrideAttrs (old: { 70 | version = "0.1.1+date=2023-03-02"; 71 | src = final.fetchFromGitLab { 72 | domain = "gitlab.freedesktop.org"; 73 | owner = "emersion"; 74 | repo = old.pname; 75 | rev = "147d6611a64a6ab04611b923e30efacaca6fc678"; 76 | sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4="; 77 | }; 78 | }); 79 | 80 | libliftoff = prev.libliftoff.overrideAttrs (old: { 81 | version = "0.5.0-dev"; 82 | src = final.fetchFromGitLab { 83 | domain = "gitlab.freedesktop.org"; 84 | owner = "emersion"; 85 | repo = old.pname; 86 | rev = "d98ae243280074b0ba44bff92215ae8d785658c0"; 87 | sha256 = "sha256-DjwlS8rXE7srs7A8+tHqXyUsFGtucYSeq6X0T/pVOc8="; 88 | }; 89 | 90 | NIX_CFLAGS_COMPILE = toString ["-Wno-error=sign-conversion"]; 91 | }); 92 | }; 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /nix/patches/meson-build.patch: -------------------------------------------------------------------------------- 1 | diff --git a/meson.build b/meson.build 2 | index 1d2c7f9f..c5ef4e67 100644 3 | --- a/meson.build 4 | +++ b/meson.build 5 | @@ -33,20 +33,7 @@ if cpp_compiler.check_header('execinfo.h') 6 | add_project_arguments('-DHAS_EXECINFO', language: 'cpp') 7 | endif 8 | 9 | -wlroots = subproject('wlroots', default_options: ['examples=false', 'renderers=gles2']) 10 | -have_xwlr = wlroots.get_variable('features').get('xwayland') 11 | -xcb_dep = dependency('xcb', required: get_option('xwayland')) 12 | - 13 | -cmake = import('cmake') 14 | -udis = cmake.subproject('udis86') 15 | -udis86 = udis.dependency('libudis86') 16 | - 17 | -if get_option('xwayland').enabled() and not have_xwlr 18 | - error('Cannot enable Xwayland in Hyprland: wlroots has been built without Xwayland support') 19 | -endif 20 | -have_xwayland = xcb_dep.found() and have_xwlr 21 | - 22 | -if not have_xwayland 23 | +if get_option('xwayland').disabled() 24 | add_project_arguments('-DNO_XWAYLAND', language: 'cpp') 25 | endif 26 | 27 | @@ -69,8 +56,6 @@ if get_option('buildtype') == 'debug' 28 | add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp') 29 | endif 30 | 31 | -version_h = run_command('sh', '-c', 'scripts/generateVersion.sh') 32 | - 33 | globber = run_command('find', 'src', '-name', '*.h*', check: true) 34 | headers = globber.stdout().strip().split('\n') 35 | foreach file : headers 36 | diff --git a/src/meson.build b/src/meson.build 37 | index 0af864b9..38723b8c 100644 38 | --- a/src/meson.build 39 | +++ b/src/meson.build 40 | @@ -9,16 +9,16 @@ executable('Hyprland', src, 41 | server_protos, 42 | dependency('wayland-server'), 43 | dependency('wayland-client'), 44 | - wlroots.get_variable('wlroots'), 45 | + dependency('wlroots'), 46 | dependency('cairo'), 47 | dependency('libdrm'), 48 | dependency('egl'), 49 | dependency('xkbcommon'), 50 | dependency('libinput'), 51 | - xcb_dep, 52 | + dependency('xcb', required: get_option('xwayland')), 53 | backtrace_dep, 54 | systemd_dep, 55 | - udis86, 56 | + dependency('udis86'), 57 | 58 | dependency('pixman-1'), 59 | dependency('gl', 'opengl'), 60 | -------------------------------------------------------------------------------- /nix/patches/wlroots-nvidia.patch: -------------------------------------------------------------------------------- 1 | diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c 2 | index 9fe934f7..9662d4ee 100644 3 | --- a/render/gles2/renderer.c 4 | +++ b/render/gles2/renderer.c 5 | @@ -176,7 +176,7 @@ static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer, 6 | assert(wlr_egl_is_current(renderer->egl)); 7 | 8 | push_gles2_debug(renderer); 9 | - glFlush(); 10 | + glFinish(); 11 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 12 | pop_gles2_debug(renderer); 13 | 14 | diff --git a/types/output/render.c b/types/output/render.c 15 | index 2e38919a..97f78608 100644 16 | --- a/types/output/render.c 17 | +++ b/types/output/render.c 18 | @@ -240,22 +240,7 @@ bool output_pick_format(struct wlr_output *output, 19 | } 20 | 21 | uint32_t wlr_output_preferred_read_format(struct wlr_output *output) { 22 | - struct wlr_renderer *renderer = output->renderer; 23 | - assert(renderer != NULL); 24 | - 25 | - if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { 26 | - return DRM_FORMAT_INVALID; 27 | - } 28 | - 29 | - if (!wlr_output_attach_render(output, NULL)) { 30 | - return false; 31 | - } 32 | - 33 | - uint32_t fmt = renderer->impl->preferred_read_format(renderer); 34 | - 35 | - output_clear_back_buffer(output); 36 | - 37 | - return fmt; 38 | + return DRM_FORMAT_XRGB8888; 39 | } 40 | 41 | struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, 42 | -------------------------------------------------------------------------------- /nix/udis86.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | fetchFromGitHub, 5 | autoreconfHook, 6 | python3, 7 | }: 8 | stdenv.mkDerivation { 9 | pname = "udis86"; 10 | version = "unstable-2022-10-13"; 11 | 12 | src = fetchFromGitHub { 13 | owner = "canihavesomecoffee"; 14 | repo = "udis86"; 15 | rev = "5336633af70f3917760a6d441ff02d93477b0c86"; 16 | hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g="; 17 | }; 18 | 19 | nativeBuildInputs = [autoreconfHook python3]; 20 | 21 | configureFlags = ["--enable-shared"]; 22 | 23 | outputs = ["bin" "out" "dev" "lib"]; 24 | 25 | meta = with lib; { 26 | homepage = "https://udis86.sourceforge.net"; 27 | license = licenses.bsd2; 28 | mainProgram = "udcli"; 29 | description = "Easy-to-use, minimalistic x86 disassembler library (libudis86)"; 30 | platforms = platforms.all; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /nix/update-inputs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S nix shell nixpkgs#jq -c bash 2 | 3 | # Update inputs when the Mesa version is outdated. We don't want 4 | # incompatibilities between the user's system and Hyprland. 5 | 6 | # get the current Nixpkgs revision 7 | REV=$(jq $NEW_VER and flake inputs" 14 | 15 | # update inputs to latest versions 16 | nix flake update 17 | else 18 | echo "nixpkgs is up to date!" 19 | fi 20 | -------------------------------------------------------------------------------- /nix/update-wlroots.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S nix shell nixpkgs#gawk nixpkgs#git nixpkgs#gnused nixpkgs#ripgrep -c bash 2 | 3 | # get wlroots revision from submodule 4 | SUB_REV=$(git submodule status | rg wlroots | awk '{ print substr($1,2) }') 5 | # and from lockfile 6 | CRT_REV=$(rg rev flake.nix | awk '{ print substr($3, 2, 40) }') 7 | 8 | if [ "$SUB_REV" != "$CRT_REV" ]; then 9 | echo "Updating wlroots..." 10 | # update wlroots to submodule revision 11 | sed -Ei "s/\w{40}/$SUB_REV/g" flake.nix subprojects/wlroots.wrap 12 | nix flake lock 13 | 14 | echo "wlroots: $CRT_REV -> $SUB_REV" 15 | else 16 | echo "wlroots is up to date!" 17 | fi 18 | -------------------------------------------------------------------------------- /nix/wlroots.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | version, 4 | src, 5 | wlroots, 6 | hwdata, 7 | libdisplay-info, 8 | libliftoff, 9 | enableXWayland ? true, 10 | enableNvidiaPatches ? false, 11 | }: 12 | wlroots.overrideAttrs (old: { 13 | inherit version src enableXWayland; 14 | 15 | pname = "${old.pname}-hyprland${lib.optionalString enableNvidiaPatches "-nvidia"}"; 16 | 17 | patches = 18 | (old.patches or []) 19 | ++ (lib.optionals enableNvidiaPatches [ 20 | ./patches/wlroots-nvidia.patch 21 | ]); 22 | 23 | buildInputs = old.buildInputs ++ [hwdata libliftoff libdisplay-info]; 24 | 25 | NIX_CFLAGS_COMPILE = toString [ 26 | "-Wno-error=maybe-uninitialized" 27 | ]; 28 | }) 29 | -------------------------------------------------------------------------------- /props.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.31.0" 3 | } -------------------------------------------------------------------------------- /protocols/idle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 18 | ]]> 19 | 20 | 21 | This interface allows to monitor user idle time on a given seat. The interface 22 | allows to register timers which trigger after no user activity was registered 23 | on the seat for a given interval. It notifies when user activity resumes. 24 | 25 | This is useful for applications wanting to perform actions when the user is not 26 | interacting with the system, e.g. chat applications setting the user as away, power 27 | management features to dim screen, etc.. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /protocols/meson.build: -------------------------------------------------------------------------------- 1 | wayland_protos = dependency('wayland-protocols', 2 | version: '>=1.25', 3 | fallback: 'wayland-protocols', 4 | default_options: ['tests=false'], 5 | ) 6 | 7 | hyprland_protos = dependency('hyprland-protocols', 8 | version: '>=0.2', 9 | fallback: 'hyprland-protocols', 10 | ) 11 | 12 | wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') 13 | hl_protocol_dir = hyprland_protos.get_variable('pkgdatadir') 14 | 15 | wayland_scanner_dep = dependency('wayland-scanner', native: true) 16 | wayland_scanner = find_program( 17 | wayland_scanner_dep.get_variable('wayland_scanner'), 18 | native: true, 19 | ) 20 | 21 | protocols = [ 22 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 23 | [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], 24 | [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], 25 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 26 | [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], 27 | [wl_protocol_dir, 'staging/cursor-shape/cursor-shape-v1.xml'], 28 | [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], 29 | ['wlr-foreign-toplevel-management-unstable-v1.xml'], 30 | ['wlr-layer-shell-unstable-v1.xml'], 31 | ['wlr-output-power-management-unstable-v1.xml'], 32 | ['wlr-screencopy-unstable-v1.xml'], 33 | ['pointer-constraints-unstable-v1.xml'], 34 | ['tablet-unstable-v2.xml'], 35 | ['idle.xml'], 36 | [hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'], 37 | [hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml'] 38 | ] 39 | wl_protos_src = [] 40 | wl_protos_headers = [] 41 | foreach p : protocols 42 | xml = join_paths(p) 43 | wl_protos_src += custom_target( 44 | xml.underscorify() + '_server_c', 45 | input: xml, 46 | output: '@BASENAME@-protocol.c', 47 | command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], 48 | ) 49 | wl_protos_headers += custom_target( 50 | xml.underscorify() + '_server_h', 51 | input: xml, 52 | install: true, 53 | install_dir: join_paths(get_option('includedir'), 'hyprland/protocols'), 54 | output: '@BASENAME@-protocol.h', 55 | command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], 56 | ) 57 | endforeach 58 | 59 | wayland_server = dependency('wayland-server', version: '>=1.20.0') 60 | 61 | lib_server_protos = static_library( 62 | 'server_protos', 63 | wl_protos_src + wl_protos_headers, 64 | dependencies: wayland_server.partial_dependency(compile_args: true), 65 | ) 66 | 67 | server_protos = declare_dependency( 68 | link_with: lib_server_protos, 69 | sources: wl_protos_headers, 70 | ) 71 | -------------------------------------------------------------------------------- /scripts/generateVersion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cp -fr ./src/version.h.in ./src/version.h 3 | 4 | HASH=$(git rev-parse HEAD) 5 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 6 | MESSAGE=$(git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g') 7 | DIRTY=$(git diff-index --quiet HEAD -- || echo dirty) 8 | TAG=$(git describe --tags) 9 | 10 | sed -i -e "s#@HASH@#${HASH}#" ./src/version.h 11 | sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h 12 | sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h 13 | sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h 14 | sed -i -e "s#@TAG@#${TAG}#" ./src/version.h -------------------------------------------------------------------------------- /src/SharedDefs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "helpers/Vector2D.hpp" 4 | 5 | enum eIcons 6 | { 7 | ICON_WARNING = 0, 8 | ICON_INFO, 9 | ICON_HINT, 10 | ICON_ERROR, 11 | ICON_CONFUSED, 12 | ICON_OK, 13 | ICON_NONE 14 | }; 15 | 16 | enum eRenderStage 17 | { 18 | RENDER_PRE = 0, /* Before binding the gl context */ 19 | RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */ 20 | RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */ 21 | RENDER_POST_WINDOWS, /* Post windows, pre top/overlay layers, etc */ 22 | RENDER_LAST_MOMENT, /* Last moment to render with the gl context */ 23 | RENDER_POST, /* After rendering is finished, gl context not available anymore */ 24 | RENDER_POST_MIRROR, /* After rendering a mirror */ 25 | RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */ 26 | RENDER_POST_WINDOW, /* After rendering a window (any pass) */ 27 | }; 28 | 29 | struct SCallbackInfo { 30 | bool cancelled = false; /* on cancellable events, will cancel the event. */ 31 | }; 32 | 33 | struct SWindowDecorationExtents { 34 | Vector2D topLeft; 35 | Vector2D bottomRight; 36 | 37 | // 38 | SWindowDecorationExtents operator*(const double& scale) const { 39 | return SWindowDecorationExtents{topLeft * scale, bottomRight * scale}; 40 | } 41 | 42 | SWindowDecorationExtents floor() { 43 | return {topLeft.floor(), bottomRight.floor()}; 44 | } 45 | }; -------------------------------------------------------------------------------- /src/config/ConfigDataValues.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../defines.hpp" 3 | #include 4 | 5 | enum eConfigValueDataTypes { 6 | CVD_TYPE_INVALID = -1, 7 | CVD_TYPE_GRADIENT = 0 8 | }; 9 | 10 | class ICustomConfigValueData { 11 | public: 12 | virtual ~ICustomConfigValueData() = 0; 13 | 14 | virtual eConfigValueDataTypes getDataType() = 0; 15 | }; 16 | 17 | class CGradientValueData : public ICustomConfigValueData { 18 | public: 19 | CGradientValueData(CColor col) { 20 | m_vColors.push_back(col); 21 | }; 22 | virtual ~CGradientValueData(){}; 23 | 24 | virtual eConfigValueDataTypes getDataType() { 25 | return CVD_TYPE_GRADIENT; 26 | } 27 | 28 | void reset(CColor col) { 29 | m_vColors.clear(); 30 | m_vColors.emplace_back(col); 31 | m_fAngle = 0; 32 | } 33 | 34 | /* Vector containing the colors */ 35 | std::vector m_vColors; 36 | 37 | /* Float corresponding to the angle (rad) */ 38 | float m_fAngle = 0; 39 | 40 | // 41 | bool operator==(const CGradientValueData& other) const { 42 | if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle) 43 | return false; 44 | 45 | for (size_t i = 0; i < m_vColors.size(); ++i) 46 | if (m_vColors[i] != other.m_vColors[i]) 47 | return false; 48 | 49 | return true; 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/debug/CrashReporter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | namespace CrashReporter { 6 | void createAndSaveCrash(int sig); 7 | }; -------------------------------------------------------------------------------- /src/debug/HyprCtl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Compositor.hpp" 4 | #include 5 | #include "../helpers/MiscFunctions.hpp" 6 | 7 | namespace HyprCtl { 8 | void startHyprCtlSocket(); 9 | std::string makeDynamicCall(const std::string& input); 10 | 11 | // very simple thread-safe request method 12 | inline bool requestMade = false; 13 | inline bool requestReady = false; 14 | inline std::string request = ""; 15 | 16 | inline std::ifstream requestStream; 17 | 18 | inline wl_event_source* hyprCtlTickSource = nullptr; 19 | 20 | inline int iSocketFD = -1; 21 | 22 | enum eHyprCtlOutputFormat { 23 | FORMAT_NORMAL = 0, 24 | FORMAT_JSON 25 | }; 26 | }; -------------------------------------------------------------------------------- /src/debug/HyprDebugOverlay.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "../helpers/Monitor.hpp" 5 | #include "../render/Texture.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | class CHyprRenderer; 11 | 12 | class CHyprMonitorDebugOverlay { 13 | public: 14 | int draw(int offset); 15 | 16 | void renderData(CMonitor* pMonitor, float µs); 17 | void renderDataNoOverlay(CMonitor* pMonitor, float µs); 18 | void frameData(CMonitor* pMonitor); 19 | 20 | private: 21 | std::deque m_dLastFrametimes; 22 | std::deque m_dLastRenderTimes; 23 | std::deque m_dLastRenderTimesNoOverlay; 24 | std::deque m_dLastAnimationTicks; 25 | std::chrono::high_resolution_clock::time_point m_tpLastFrame; 26 | CMonitor* m_pMonitor = nullptr; 27 | CBox m_wbLastDrawnBox; 28 | 29 | friend class CHyprRenderer; 30 | }; 31 | 32 | class CHyprDebugOverlay { 33 | public: 34 | void draw(); 35 | void renderData(CMonitor*, float µs); 36 | void renderDataNoOverlay(CMonitor*, float µs); 37 | void frameData(CMonitor*); 38 | 39 | private: 40 | std::unordered_map m_mMonitorOverlays; 41 | 42 | cairo_surface_t* m_pCairoSurface = nullptr; 43 | cairo_t* m_pCairo = nullptr; 44 | 45 | CTexture m_tTexture; 46 | 47 | friend class CHyprMonitorDebugOverlay; 48 | friend class CHyprRenderer; 49 | }; 50 | 51 | inline std::unique_ptr g_pDebugOverlay; -------------------------------------------------------------------------------- /src/debug/HyprNotificationOverlay.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "../helpers/Timer.hpp" 5 | #include "../helpers/Monitor.hpp" 6 | #include "../render/Texture.hpp" 7 | #include "../SharedDefs.hpp" 8 | 9 | #include 10 | 11 | #include 12 | 13 | enum eIconBackend 14 | { 15 | ICONS_BACKEND_NONE = 0, 16 | ICONS_BACKEND_NF, 17 | ICONS_BACKEND_FA 18 | }; 19 | 20 | static const std::array, 3 /* backends */> ICONS_ARRAY = { 21 | std::array{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array{"", "", "", "", "", "󰸞", ""}, 22 | std::array{"", "", "", "", "", ""}}; 23 | static const std::array ICONS_COLORS = {CColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0}, 24 | CColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0}, 25 | CColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0}, 26 | CColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0}, 27 | CColor{255 / 255.0, 204 / 255.0, 153 / 255.0, 1.0}, 28 | CColor{128 / 255.0, 255 / 255.0, 128 / 255.0, 1.0}, 29 | CColor{0, 0, 0, 1.0}}; 30 | 31 | struct SNotification { 32 | std::string text = ""; 33 | CColor color; 34 | CTimer started; 35 | float timeMs = 0; 36 | eIcons icon = ICON_NONE; 37 | }; 38 | 39 | class CHyprNotificationOverlay { 40 | public: 41 | CHyprNotificationOverlay(); 42 | 43 | void draw(CMonitor* pMonitor); 44 | void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE); 45 | 46 | private: 47 | CBox drawNotifications(CMonitor* pMonitor); 48 | CBox m_bLastDamage; 49 | 50 | std::deque> m_dNotifications; 51 | 52 | cairo_surface_t* m_pCairoSurface = nullptr; 53 | cairo_t* m_pCairo = nullptr; 54 | 55 | CMonitor* m_pLastMonitor = nullptr; 56 | 57 | CTexture m_tTexture; 58 | 59 | eIconBackend m_eIconBackend = ICONS_BACKEND_NONE; 60 | std::string m_szIconFontName = "Sans"; 61 | }; 62 | 63 | inline std::unique_ptr g_pHyprNotificationOverlay; -------------------------------------------------------------------------------- /src/debug/Log.cpp: -------------------------------------------------------------------------------- 1 | #include "Log.hpp" 2 | #include "../defines.hpp" 3 | #include "../Compositor.hpp" 4 | 5 | #include 6 | #include 7 | 8 | void Debug::init(const std::string& IS) { 9 | logFile = "/tmp/hypr/" + IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log"); 10 | } 11 | 12 | void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) { 13 | if (disableLogs && *disableLogs) 14 | return; 15 | 16 | if (level > wlr_log_get_verbosity()) 17 | return; 18 | 19 | char* outputStr = nullptr; 20 | 21 | std::ofstream ofs; 22 | ofs.open(logFile, std::ios::out | std::ios::app); 23 | 24 | vasprintf(&outputStr, fmt, args); 25 | 26 | std::string output = std::string(outputStr); 27 | free(outputStr); 28 | 29 | ofs << "[wlr] " << output << "\n"; 30 | 31 | ofs.close(); 32 | 33 | if (!disableStdout) 34 | std::cout << output << "\n"; 35 | } 36 | -------------------------------------------------------------------------------- /src/debug/Log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../includes.hpp" 8 | #include "../helpers/MiscFunctions.hpp" 9 | 10 | #define LOGMESSAGESIZE 1024 11 | 12 | enum LogLevel { 13 | NONE = -1, 14 | LOG = 0, 15 | WARN, 16 | ERR, 17 | CRIT, 18 | INFO, 19 | TRACE 20 | }; 21 | 22 | namespace Debug { 23 | inline std::string logFile; 24 | inline int64_t* disableLogs = nullptr; 25 | inline int64_t* disableTime = nullptr; 26 | inline bool disableStdout = false; 27 | inline bool trace = false; 28 | 29 | void init(const std::string& IS); 30 | template 31 | void log(LogLevel level, std::format_string fmt, Args&&... args) { 32 | if (disableLogs && *disableLogs) 33 | return; 34 | 35 | if (level == TRACE && !trace) 36 | return; 37 | 38 | std::string logMsg = ""; 39 | 40 | switch (level) { 41 | case LOG: logMsg += "[LOG] "; break; 42 | case WARN: logMsg += "[WARN] "; break; 43 | case ERR: logMsg += "[ERR] "; break; 44 | case CRIT: logMsg += "[CRITICAL] "; break; 45 | case INFO: logMsg += "[INFO] "; break; 46 | case TRACE: logMsg += "[TRACE] "; break; 47 | default: break; 48 | } 49 | 50 | // log to a file 51 | std::ofstream ofs; 52 | ofs.open(logFile, std::ios::out | std::ios::app); 53 | 54 | // print date and time to the ofs 55 | if (disableTime && !*disableTime) { 56 | #ifndef _LIBCPP_VERSION 57 | logMsg += std::format("[{:%T}] ", std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}); 58 | #else 59 | auto c = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; 60 | logMsg += std::format("{:%H}:{:%M}:{:%S}", c.hours(), c.minutes(), c.subseconds()); 61 | 62 | #endif 63 | } 64 | 65 | // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error 66 | // because 67 | // 1. any faulty format specifier that sucks will cause a compilation error. 68 | // 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.) 69 | // 3. this is actually what std::format in stdlib does 70 | logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); 71 | 72 | ofs << logMsg << "\n"; 73 | 74 | ofs.close(); 75 | 76 | // log it to the stdout too. 77 | if (!disableStdout) 78 | std::cout << logMsg << "\n"; 79 | } 80 | 81 | void wlrLog(wlr_log_importance level, const char* fmt, va_list args); 82 | }; 83 | -------------------------------------------------------------------------------- /src/debug/TracyDefines.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef USE_TRACY_GPU 4 | 5 | #include "Log.hpp" 6 | 7 | #include 8 | #include 9 | 10 | inline PFNGLQUERYCOUNTEREXTPROC glQueryCounter; 11 | inline PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectiv; 12 | inline PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v; 13 | 14 | #include "../../subprojects/tracy/public/tracy/TracyOpenGL.hpp" 15 | 16 | inline void loadGLProc(void* pProc, const char* name) { 17 | void* proc = (void*)eglGetProcAddress(name); 18 | if (proc == NULL) { 19 | Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name); 20 | abort(); 21 | } 22 | *(void**)pProc = proc; 23 | } 24 | 25 | #define TRACY_GPU_CONTEXT TracyGpuContext 26 | #define TRACY_GPU_ZONE(e) TracyGpuZone(e) 27 | #define TRACY_GPU_COLLECT TracyGpuCollect 28 | 29 | #else 30 | 31 | #define TRACY_GPU_CONTEXT 32 | #define TRACY_GPU_ZONE(e) 33 | #define TRACY_GPU_COLLECT 34 | 35 | #endif -------------------------------------------------------------------------------- /src/defines.hpp: -------------------------------------------------------------------------------- 1 | #include "includes.hpp" 2 | #include "debug/Log.hpp" 3 | #include "helpers/WLListener.hpp" 4 | #include "helpers/Color.hpp" 5 | #include "macros.hpp" 6 | -------------------------------------------------------------------------------- /src/helpers/AnimatedVariable.cpp: -------------------------------------------------------------------------------- 1 | #include "AnimatedVariable.hpp" 2 | #include "../managers/AnimationManager.hpp" 3 | #include "../config/ConfigManager.hpp" 4 | 5 | CAnimatedVariable::CAnimatedVariable() { 6 | ; // dummy var 7 | } 8 | 9 | void CAnimatedVariable::create(ANIMATEDVARTYPE type, SAnimationPropertyConfig* pAnimConfig, void* pWindow, AVARDAMAGEPOLICY policy) { 10 | m_eVarType = type; 11 | m_eDamagePolicy = policy; 12 | m_pConfig = pAnimConfig; 13 | m_pWindow = pWindow; 14 | 15 | m_bDummy = false; 16 | } 17 | 18 | void CAnimatedVariable::create(ANIMATEDVARTYPE type, std::any val, SAnimationPropertyConfig* pAnimConfig, void* pWindow, AVARDAMAGEPOLICY policy) { 19 | create(type, pAnimConfig, pWindow, policy); 20 | 21 | try { 22 | switch (type) { 23 | case AVARTYPE_FLOAT: { 24 | const auto V = std::any_cast(val); 25 | m_fValue = V; 26 | m_fGoal = V; 27 | break; 28 | } 29 | case AVARTYPE_VECTOR: { 30 | const auto V = std::any_cast(val); 31 | m_vValue = V; 32 | m_vGoal = V; 33 | break; 34 | } 35 | case AVARTYPE_COLOR: { 36 | const auto V = std::any_cast(val); 37 | m_cValue = V; 38 | m_cGoal = V; 39 | break; 40 | } 41 | default: ASSERT(false); break; 42 | } 43 | } catch (std::exception& e) { 44 | Debug::log(ERR, "CAnimatedVariable create error: {}", e.what()); 45 | RASSERT(false, "CAnimatedVariable create error: {}", e.what()); 46 | } 47 | } 48 | 49 | CAnimatedVariable::~CAnimatedVariable() { 50 | unregister(); 51 | } 52 | 53 | void CAnimatedVariable::unregister() { 54 | if (!g_pAnimationManager) 55 | return; 56 | std::erase_if(g_pAnimationManager->m_vAnimatedVariables, [&](const auto& other) { return other == this; }); 57 | m_bIsRegistered = false; 58 | disconnectFromActive(); 59 | } 60 | 61 | void CAnimatedVariable::registerVar() { 62 | if (!m_bIsRegistered) 63 | g_pAnimationManager->m_vAnimatedVariables.push_back(this); 64 | m_bIsRegistered = true; 65 | } 66 | 67 | int CAnimatedVariable::getDurationLeftMs() { 68 | return std::max( 69 | (int)(m_pConfig->pValues->internalSpeed * 100) - (int)std::chrono::duration_cast(std::chrono::system_clock::now() - animationBegin).count(), 0); 70 | } 71 | 72 | float CAnimatedVariable::getPercent() { 73 | const auto DURATIONPASSED = std::chrono::duration_cast(std::chrono::system_clock::now() - animationBegin).count(); 74 | return std::clamp((DURATIONPASSED / 100.f) / m_pConfig->pValues->internalSpeed, 0.f, 1.f); 75 | } 76 | 77 | float CAnimatedVariable::getCurveValue() { 78 | const auto SPENT = getPercent(); 79 | 80 | if (SPENT >= 1.f) 81 | return 1.f; 82 | 83 | return g_pAnimationManager->getBezier(m_pConfig->pValues->internalBezier)->getYForPoint(SPENT); 84 | } 85 | 86 | void CAnimatedVariable::connectToActive() { 87 | g_pAnimationManager->scheduleTick(); // otherwise the animation manager will never pick this up 88 | 89 | if (!m_bIsConnectedToActive) 90 | g_pAnimationManager->m_vActiveAnimatedVariables.push_back(this); 91 | 92 | m_bIsConnectedToActive = true; 93 | } 94 | 95 | void CAnimatedVariable::disconnectFromActive() { 96 | std::erase_if(g_pAnimationManager->m_vActiveAnimatedVariables, [&](const auto& other) { return other == this; }); 97 | m_bIsConnectedToActive = false; 98 | } -------------------------------------------------------------------------------- /src/helpers/BezierCurve.cpp: -------------------------------------------------------------------------------- 1 | #include "BezierCurve.hpp" 2 | #include "../debug/Log.hpp" 3 | #include "../macros.hpp" 4 | 5 | #include 6 | #include 7 | 8 | void CBezierCurve::setup(std::vector* pVec) { 9 | m_dPoints.clear(); 10 | 11 | const auto BEGIN = std::chrono::high_resolution_clock::now(); 12 | 13 | m_dPoints.emplace_back(Vector2D(0, 0)); 14 | 15 | for (auto& p : *pVec) { 16 | m_dPoints.push_back(p); 17 | } 18 | 19 | m_dPoints.emplace_back(Vector2D(1, 1)); 20 | 21 | RASSERT(m_dPoints.size() == 4, "CBezierCurve only supports cubic beziers! (points num: {})", m_dPoints.size()); 22 | 23 | // bake BAKEDPOINTS points for faster lookups 24 | // T -> X ( / BAKEDPOINTS ) 25 | for (int i = 0; i < BAKEDPOINTS; ++i) { 26 | m_aPointsBaked[i] = Vector2D(getXForT((i + 1) / (float)BAKEDPOINTS), getYForT((i + 1) / (float)BAKEDPOINTS)); 27 | } 28 | 29 | const auto ELAPSEDUS = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - BEGIN).count() / 1000.f; 30 | const auto POINTSSIZE = m_aPointsBaked.size() * sizeof(m_aPointsBaked[0]) / 1000.f; 31 | 32 | const auto BEGINCALC = std::chrono::high_resolution_clock::now(); 33 | for (float i = 0.1f; i < 1.f; i += 0.1f) 34 | getYForPoint(i); 35 | const auto ELAPSEDCALCAVG = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - BEGINCALC).count() / 1000.f / 10.f; 36 | 37 | Debug::log(LOG, "Created a bezier curve, baked {} points, mem usage: {:.2f}kB, time to bake: {:.2f}µs. Estimated average calc time: {:.2f}µs.", BAKEDPOINTS, POINTSSIZE, 38 | ELAPSEDUS, ELAPSEDCALCAVG); 39 | } 40 | 41 | float CBezierCurve::getYForT(float t) { 42 | return 3 * t * pow(1 - t, 2) * m_dPoints[1].y + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].y + pow(t, 3); 43 | } 44 | 45 | float CBezierCurve::getXForT(float t) { 46 | return 3 * t * pow(1 - t, 2) * m_dPoints[1].x + 3 * pow(t, 2) * (1 - t) * m_dPoints[2].x + pow(t, 3); 47 | } 48 | 49 | // Todo: this probably can be done better and faster 50 | float CBezierCurve::getYForPoint(float x) { 51 | if (x >= 1.f) 52 | return 1.f; 53 | 54 | int index = 0; 55 | bool below = true; 56 | for (int step = (BAKEDPOINTS + 1) / 2; step > 0; step /= 2) { 57 | if (below) 58 | index += step; 59 | else 60 | index -= step; 61 | 62 | below = m_aPointsBaked[index].x < x; 63 | } 64 | 65 | int lowerIndex = index - (!below || index == BAKEDPOINTS - 1); 66 | 67 | // in the name of performance i shall make a hack 68 | const auto LOWERPOINT = &m_aPointsBaked[lowerIndex]; 69 | const auto UPPERPOINT = &m_aPointsBaked[lowerIndex + 1]; 70 | 71 | const auto PERCINDELTA = (x - LOWERPOINT->x) / (UPPERPOINT->x - LOWERPOINT->x); 72 | 73 | if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) // can sometimes happen for VERY small x 74 | return 0.f; 75 | 76 | return LOWERPOINT->y + (UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA; 77 | } 78 | -------------------------------------------------------------------------------- /src/helpers/BezierCurve.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Vector2D.hpp" 7 | 8 | constexpr int BAKEDPOINTS = 255; 9 | constexpr float INVBAKEDPOINTS = 1.f / BAKEDPOINTS; 10 | 11 | // an implementation of a cubic bezier curve 12 | // might do better later 13 | class CBezierCurve { 14 | public: 15 | // sets up the bezier curve. 16 | // this EXCLUDES the 0,0 and 1,1 points, 17 | void setup(std::vector* points); 18 | 19 | float getYForT(float t); 20 | float getXForT(float t); 21 | float getYForPoint(float x); 22 | 23 | private: 24 | // this INCLUDES the 0,0 and 1,1 points. 25 | std::deque m_dPoints; 26 | 27 | std::array m_aPointsBaked; 28 | }; 29 | -------------------------------------------------------------------------------- /src/helpers/Box.cpp: -------------------------------------------------------------------------------- 1 | #include "Box.hpp" 2 | wlr_box CBox::wlr() { 3 | CBox rounded = roundInternal(); 4 | m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h}; 5 | return m_bWlrBox; 6 | } 7 | 8 | wlr_box* CBox::pWlr() { 9 | CBox rounded = roundInternal(); 10 | m_bWlrBox = wlr_box{(int)rounded.x, (int)rounded.y, (int)rounded.w, (int)rounded.h}; 11 | return &m_bWlrBox; 12 | } 13 | 14 | CBox& CBox::scale(double scale) { 15 | x *= scale; 16 | y *= scale; 17 | w *= scale; 18 | h *= scale; 19 | 20 | return *this; 21 | } 22 | 23 | CBox& CBox::scale(const Vector2D& scale) { 24 | x *= scale.x; 25 | y *= scale.y; 26 | w *= scale.x; 27 | h *= scale.y; 28 | 29 | return *this; 30 | } 31 | 32 | CBox& CBox::translate(const Vector2D& vec) { 33 | x += vec.x; 34 | y += vec.y; 35 | 36 | return *this; 37 | } 38 | 39 | Vector2D CBox::middle() const { 40 | return Vector2D{x + w / 2.0, y + h / 2.0}; 41 | } 42 | 43 | bool CBox::containsPoint(const Vector2D& vec) const { 44 | return VECINRECT(vec, x, y, x + w, y + h); 45 | } 46 | 47 | bool CBox::empty() const { 48 | return w == 0 || h == 0; 49 | } 50 | 51 | CBox& CBox::applyFromWlr() { 52 | x = m_bWlrBox.x; 53 | y = m_bWlrBox.y; 54 | w = m_bWlrBox.width; 55 | h = m_bWlrBox.height; 56 | 57 | return *this; 58 | } 59 | 60 | CBox& CBox::round() { 61 | float newW = x + w - std::round(x); 62 | float newH = y + h - std::round(y); 63 | x = std::round(x); 64 | y = std::round(y); 65 | w = std::round(newW); 66 | h = std::round(newH); 67 | 68 | return *this; 69 | } 70 | 71 | CBox& CBox::transform(const wl_output_transform t, double w, double h) { 72 | wlr_box_transform(&m_bWlrBox, pWlr(), t, w, h); 73 | applyFromWlr(); 74 | 75 | return *this; 76 | } 77 | 78 | CBox& CBox::addExtents(const SWindowDecorationExtents& e) { 79 | x -= e.topLeft.x; 80 | y -= e.topLeft.y; 81 | w += e.topLeft.x + e.bottomRight.x; 82 | h += e.topLeft.y + e.bottomRight.y; 83 | 84 | return *this; 85 | } 86 | 87 | CBox& CBox::scaleFromCenter(double scale) { 88 | double oldW = w, oldH = h; 89 | 90 | w *= scale; 91 | h *= scale; 92 | 93 | x -= (w - oldW) / 2.0; 94 | y -= (h - oldH) / 2.0; 95 | 96 | return *this; 97 | } 98 | 99 | CBox CBox::roundInternal() { 100 | float newW = x + w - std::floor(x); 101 | float newH = y + h - std::floor(y); 102 | 103 | return CBox{std::floor(x), std::floor(y), std::floor(newW), std::floor(newH)}; 104 | } 105 | 106 | Vector2D CBox::pos() const { 107 | return {x, y}; 108 | } 109 | 110 | Vector2D CBox::size() const { 111 | return {w, h}; 112 | } 113 | -------------------------------------------------------------------------------- /src/helpers/Box.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Vector2D.hpp" 4 | #include "../SharedDefs.hpp" 5 | #include "../includes.hpp" 6 | 7 | class CBox { 8 | public: 9 | CBox(double x_, double y_, double w_, double h_) { 10 | x = x_; 11 | y = y_; 12 | w = w_; 13 | h = h_; 14 | } 15 | 16 | CBox() { 17 | w = 0; 18 | h = 0; 19 | } 20 | 21 | CBox(const wlr_box& box) { 22 | x = box.x; 23 | y = box.y; 24 | w = box.width; 25 | h = box.height; 26 | } 27 | 28 | CBox(const double d) { 29 | x = d; 30 | y = d; 31 | w = d; 32 | h = d; 33 | } 34 | 35 | CBox(const Vector2D& pos, const Vector2D& size) { 36 | x = pos.x; 37 | y = pos.y; 38 | w = size.x; 39 | h = size.y; 40 | } 41 | 42 | wlr_box wlr(); 43 | wlr_box* pWlr(); 44 | 45 | CBox& applyFromWlr(); 46 | CBox& scale(double scale); 47 | CBox& scaleFromCenter(double scale); 48 | CBox& scale(const Vector2D& scale); 49 | CBox& translate(const Vector2D& vec); 50 | CBox& round(); 51 | CBox& transform(const wl_output_transform t, double w, double h); 52 | CBox& addExtents(const SWindowDecorationExtents& e); 53 | 54 | Vector2D middle() const; 55 | Vector2D pos() const; 56 | Vector2D size() const; 57 | 58 | bool containsPoint(const Vector2D& vec) const; 59 | bool empty() const; 60 | 61 | double x = 0, y = 0; 62 | union { 63 | double w; 64 | double width; 65 | }; 66 | union { 67 | double h; 68 | double height; 69 | }; 70 | 71 | // 72 | bool operator==(const CBox& rhs) const { 73 | return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h; 74 | } 75 | 76 | private: 77 | CBox roundInternal(); 78 | 79 | wlr_box m_bWlrBox; 80 | }; 81 | -------------------------------------------------------------------------------- /src/helpers/Color.cpp: -------------------------------------------------------------------------------- 1 | #include "Color.hpp" 2 | 3 | #define ALPHA(c) ((double)(((c) >> 24) & 0xff) / 255.0) 4 | #define RED(c) ((double)(((c) >> 16) & 0xff) / 255.0) 5 | #define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0) 6 | #define BLUE(c) ((double)(((c)) & 0xff) / 255.0) 7 | 8 | CColor::CColor() {} 9 | 10 | CColor::CColor(float r, float g, float b, float a) { 11 | this->r = r; 12 | this->g = g; 13 | this->b = b; 14 | this->a = a; 15 | } 16 | 17 | CColor::CColor(uint64_t hex) { 18 | this->r = RED(hex); 19 | this->g = GREEN(hex); 20 | this->b = BLUE(hex); 21 | this->a = ALPHA(hex); 22 | } 23 | 24 | uint64_t CColor::getAsHex() { 25 | return ((int)a) * 0x1000000 + ((int)r) * 0x10000 + ((int)g) * 0x100 + ((int)b) * 0x1; 26 | } -------------------------------------------------------------------------------- /src/helpers/Color.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class CColor { 6 | public: 7 | CColor(); 8 | CColor(float, float, float, float); 9 | CColor(uint64_t); 10 | 11 | float r = 0, g = 0, b = 0, a = 1.f; 12 | 13 | uint64_t getAsHex(); 14 | 15 | CColor operator-(const CColor& c2) const { 16 | return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a); 17 | } 18 | 19 | CColor operator+(const CColor& c2) const { 20 | return CColor(r + c2.r, g + c2.g, b + c2.b, a + c2.a); 21 | } 22 | 23 | CColor operator*(const float& v) const { 24 | return CColor(r * v, g * v, b * v, a * v); 25 | } 26 | 27 | bool operator==(const CColor& c2) const { 28 | return r == c2.r && g == c2.g && b == c2.b && a == c2.a; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/helpers/MiscFunctions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Vector2D.hpp" 8 | #include 9 | #include 10 | 11 | struct SCallstackFrameInfo { 12 | void* adr = nullptr; 13 | std::string desc; 14 | }; 15 | 16 | std::string absolutePath(const std::string&, const std::string&); 17 | void addWLSignal(wl_signal*, wl_listener*, void* pOwner, const std::string& ownerString); 18 | std::string escapeJSONStrings(const std::string& str); 19 | std::string removeBeginEndSpacesTabs(std::string); 20 | bool isNumber(const std::string&, bool allowfloat = false); 21 | bool isDirection(const std::string&); 22 | bool isDirection(const char&); 23 | int getWorkspaceIDFromString(const std::string&, std::string&); 24 | std::optional cleanCmdForWorkspace(const std::string&, std::string); 25 | float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); 26 | void logSystemInfo(); 27 | std::string execAndGet(const char*); 28 | int64_t getPPIDof(int64_t pid); 29 | int64_t configStringToInt(const std::string&); 30 | float getPlusMinusKeywordResult(std::string in, float relative); 31 | void matrixProjection(float mat[9], int w, int h, wl_output_transform tr); 32 | double normalizeAngleRad(double ang); 33 | std::string replaceInString(std::string subject, const std::string& search, const std::string& replace); 34 | std::vector getBacktrace(); 35 | void throwError(const std::string& err); 36 | uint32_t drmFormatToGL(uint32_t drm); 37 | 38 | template 39 | [[deprecated("use std::format instead")]] std::string getFormat(std::format_string fmt, Args&&... args) { 40 | // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error 41 | // because any suck format specifier will cause a compilation error 42 | // this is actually what std::format in stdlib does 43 | return std::vformat(fmt.get(), std::make_format_args(args...)); 44 | } -------------------------------------------------------------------------------- /src/helpers/Region.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "Vector2D.hpp" 5 | #include "Box.hpp" 6 | 7 | struct wlr_box; 8 | 9 | class CRegion { 10 | public: 11 | /* Create an empty region */ 12 | CRegion(); 13 | /* Create from a reference. Copies, does not own. */ 14 | CRegion(pixman_region32_t* ref); 15 | /* Create from a box */ 16 | CRegion(double x, double y, double w, double h); 17 | /* Create from a wlr_box */ 18 | CRegion(wlr_box* box); 19 | /* Create from a CBox */ 20 | CRegion(CBox* box); 21 | /* Create from a pixman_box32_t */ 22 | CRegion(pixman_box32_t* box); 23 | 24 | CRegion(const CRegion&); 25 | CRegion(CRegion&&); 26 | 27 | ~CRegion(); 28 | 29 | CRegion& operator=(CRegion&& other) { 30 | pixman_region32_copy(&m_rRegion, other.pixman()); 31 | return *this; 32 | } 33 | 34 | CRegion& operator=(CRegion& other) { 35 | pixman_region32_copy(&m_rRegion, other.pixman()); 36 | return *this; 37 | } 38 | 39 | CRegion& clear(); 40 | CRegion& set(const CRegion& other); 41 | CRegion& add(const CRegion& other); 42 | CRegion& add(double x, double y, double w, double h); 43 | CRegion& subtract(const CRegion& other); 44 | CRegion& intersect(const CRegion& other); 45 | CRegion& intersect(double x, double y, double w, double h); 46 | CRegion& translate(const Vector2D& vec); 47 | CRegion& invert(pixman_box32_t* box); 48 | CRegion& scale(float scale); 49 | CBox getExtents(); 50 | bool containsPoint(const Vector2D& vec) const; 51 | bool empty() const; 52 | Vector2D closestPoint(const Vector2D& vec) const; 53 | 54 | std::vector getRects() const; 55 | 56 | pixman_region32_t* pixman() { 57 | return &m_rRegion; 58 | } 59 | 60 | private: 61 | pixman_region32_t m_rRegion; 62 | }; 63 | -------------------------------------------------------------------------------- /src/helpers/Splashes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | inline const std::vector SPLASHES = { 7 | // clang-format off 8 | "Woo, animations!", 9 | "It's like Hypr, but better.", 10 | "Release 1.0 when?", 11 | "It's not awesome, it's Hyprland!", 12 | "\"I commit too often, people can't catch up lmao\" - Vaxry", 13 | "This text is random.", 14 | "\"There are reasons to not use rust.\" - Boga", 15 | "Read the wiki.", 16 | "\"Hello everyone this is YOUR daily dose of ‘read the wiki’\" - Vaxry", 17 | "h", 18 | "\"‘why no work’, bro I haven't hacked your pc to get live feeds yet\" - Vaxry", 19 | "Compile, wait for 20 minutes, notice a new commit, compile again.", 20 | "To rice, or not to rice, that is the question.", 21 | "Now available on Fedora!", 22 | // music reference / quote section 23 | "J'remue le ciel, le jour, la nuit.", 24 | "aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!", 25 | "Wir sind schon sehr lang zusammen...", 26 | "I see a red door and I want it painted black.", 27 | "Take on me, take me on...", 28 | "You spin me right round baby right round", 29 | "Stayin' alive, stayin' alive", 30 | "Say no way, say no way ya, no way!", 31 | "Ground control to Major Tom...", 32 | "Alors on danse", 33 | "And all that I can see, is just a yellow lemon tree.", 34 | "Got a one-way ticket to the blues", 35 | "Is this the real life, is this just fantasy", 36 | "What's in your head, in your head?", 37 | "We're all living in America, America, America.", 38 | "I'm still standing, better than I ever did", 39 | "Here comes the sun, bringing you love and shining on everyone", 40 | "Two trailer park girls go round the outside", 41 | "With the lights out, it's less dangerous", 42 | "Here we go back, this is the moment, tonight is the night", 43 | "Now you're just somebody that I used to know...", 44 | "Black bird, black moon, black sky", 45 | "Some legends are told, some turn to dust or to gold", 46 | "Your brain gets smart, but your head gets dumb.", 47 | "Save your mercy for someone who needs it more", 48 | "You're gonna hear my voice when I shout it out loud", 49 | "Ding ding pch n daa, bam-ba-ba-re-bam baram bom bom baba-bam-bam-bommm", 50 | "Súbeme la radio que esta es mi canción", 51 | "I'm beggin', beggin' you", 52 | "Never gonna let you down (I am trying!)", 53 | "\"I use Arch, btw\" - John Cena", 54 | "\"Hyper\".replace(\"e\", \"\")", 55 | "\"my win11 install runs hyprland that is true\" - raf", 56 | "\"stop playing league loser\" - hyprBot", 57 | "\"If it ain't broke, don't fix it\" - Lucascito_03", 58 | "\"@vaxry how do i learn c++\" - flicko", 59 | // 60 | "Join the discord server!", 61 | "Thanks ThatOneCalculator!", 62 | "The AUR packages always work, except for the times they don't.", 63 | "Funny animation compositor woo" 64 | // clang-format on 65 | }; -------------------------------------------------------------------------------- /src/helpers/SubsurfaceTree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include 5 | #include "WLSurface.hpp" 6 | 7 | struct SSubsurface; 8 | class CWindow; 9 | 10 | typedef void (*applyGlobalOffsetFn)(void*, int*, int*); 11 | 12 | struct SSurfaceTreeNode { 13 | CWLSurface* pSurface = nullptr; // actual surface 14 | CWLSurface pInternalSurface; // not present for head nodes to not dupe wlr_surface ownership 15 | 16 | DYNLISTENER(newSubsurface); 17 | DYNLISTENER(commit); 18 | DYNLISTENER(destroy); 19 | 20 | SSurfaceTreeNode* pParent = nullptr; 21 | SSubsurface* pSubsurface = nullptr; 22 | 23 | std::list childSubsurfaces; 24 | 25 | applyGlobalOffsetFn offsetfn; 26 | void* globalOffsetData; 27 | CWindow* pWindowOwner = nullptr; 28 | 29 | // 30 | bool operator==(const SSurfaceTreeNode& rhs) const { 31 | return pSurface == rhs.pSurface; 32 | } 33 | }; 34 | 35 | struct SSubsurface { 36 | wlr_subsurface* pSubsurface = nullptr; 37 | 38 | SSurfaceTreeNode* pParent = nullptr; 39 | SSurfaceTreeNode* pChild = nullptr; 40 | 41 | DYNLISTENER(map); 42 | DYNLISTENER(unmap); 43 | DYNLISTENER(destroy); 44 | 45 | CWindow* pWindowOwner = nullptr; 46 | 47 | // 48 | bool operator==(const SSubsurface& rhs) const { 49 | return pSubsurface == rhs.pSubsurface; 50 | } 51 | }; 52 | 53 | namespace SubsurfaceTree { 54 | SSurfaceTreeNode* createTreeRoot(wlr_surface*, applyGlobalOffsetFn, void*, CWindow* pWindow = nullptr); 55 | void destroySurfaceTree(SSurfaceTreeNode*); 56 | 57 | inline std::list surfaceTreeNodes; 58 | }; 59 | -------------------------------------------------------------------------------- /src/helpers/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.hpp" 2 | 3 | void CTimer::reset() { 4 | m_tpLastReset = std::chrono::system_clock::now(); 5 | } 6 | 7 | std::chrono::system_clock::duration CTimer::getDuration() { 8 | return std::chrono::system_clock::now() - m_tpLastReset; 9 | } 10 | 11 | int CTimer::getMillis() { 12 | return std::chrono::duration_cast(getDuration()).count(); 13 | } 14 | 15 | float CTimer::getSeconds() { 16 | return std::chrono::duration_cast(getDuration()).count() / 1000.f; 17 | } 18 | 19 | const std::chrono::system_clock::time_point& CTimer::chrono() const { 20 | return m_tpLastReset; 21 | } -------------------------------------------------------------------------------- /src/helpers/Timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class CTimer { 6 | public: 7 | void reset(); 8 | float getSeconds(); 9 | int getMillis(); 10 | const std::chrono::system_clock::time_point& chrono() const; 11 | 12 | private: 13 | std::chrono::system_clock::time_point m_tpLastReset; 14 | 15 | std::chrono::system_clock::duration getDuration(); 16 | }; -------------------------------------------------------------------------------- /src/helpers/VarList.cpp: -------------------------------------------------------------------------------- 1 | #include "MiscFunctions.hpp" 2 | #include "VarList.hpp" 3 | #include 4 | #include 5 | 6 | CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) { 7 | if (in.empty()) 8 | m_vArgs.emplace_back(""); 9 | 10 | std::string args{in}; 11 | size_t idx = 0; 12 | size_t pos = 0; 13 | std::ranges::replace_if( 14 | args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0); 15 | 16 | for (const auto& s : args | std::views::split(0)) { 17 | if (removeEmpty && s.empty()) 18 | continue; 19 | if (++idx == lastArgNo) { 20 | m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos))); 21 | break; 22 | } 23 | pos += s.size() + 1; 24 | m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data())); 25 | } 26 | } 27 | 28 | std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const { 29 | size_t last = to == 0 ? size() : to; 30 | 31 | std::string rolling; 32 | for (size_t i = from; i < last; ++i) { 33 | rolling += m_vArgs[i] + (i + 1 < last ? joiner : ""); 34 | } 35 | 36 | return rolling; 37 | } -------------------------------------------------------------------------------- /src/helpers/VarList.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "../macros.hpp" 6 | 7 | class CVarList { 8 | public: 9 | /** Split string into arg list 10 | @param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args 11 | @param delim if delimiter is 's', use std::isspace 12 | @param removeEmpty remove empty args from argv 13 | */ 14 | CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false); 15 | 16 | ~CVarList() = default; 17 | 18 | size_t size() const { 19 | return m_vArgs.size(); 20 | } 21 | 22 | std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const; 23 | 24 | void map(std::function func) { 25 | for (auto& s : m_vArgs) 26 | func(s); 27 | } 28 | 29 | void append(const std::string arg) { 30 | m_vArgs.emplace_back(arg); 31 | } 32 | 33 | std::string operator[](const size_t& idx) const { 34 | if (idx >= m_vArgs.size()) 35 | return ""; 36 | return m_vArgs[idx]; 37 | } 38 | 39 | // for range-based loops 40 | std::vector::iterator begin() { 41 | return m_vArgs.begin(); 42 | } 43 | std::vector::const_iterator begin() const { 44 | return m_vArgs.begin(); 45 | } 46 | std::vector::iterator end() { 47 | return m_vArgs.end(); 48 | } 49 | std::vector::const_iterator end() const { 50 | return m_vArgs.end(); 51 | } 52 | 53 | private: 54 | std::vector m_vArgs; 55 | }; -------------------------------------------------------------------------------- /src/helpers/Vector2D.cpp: -------------------------------------------------------------------------------- 1 | #include "Vector2D.hpp" 2 | #include 3 | #include 4 | 5 | Vector2D::Vector2D(double xx, double yy) { 6 | x = xx; 7 | y = yy; 8 | } 9 | 10 | Vector2D::Vector2D() { 11 | x = 0; 12 | y = 0; 13 | } 14 | 15 | Vector2D::~Vector2D() {} 16 | 17 | double Vector2D::normalize() { 18 | // get max abs 19 | const auto max = std::abs(x) > std::abs(y) ? std::abs(x) : std::abs(y); 20 | 21 | x /= max; 22 | y /= max; 23 | 24 | return max; 25 | } 26 | 27 | Vector2D Vector2D::floor() const { 28 | return Vector2D(std::floor(x), std::floor(y)); 29 | } 30 | 31 | Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const { 32 | return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y)); 33 | } 34 | 35 | double Vector2D::distance(const Vector2D& other) const { 36 | double dx = x - other.x; 37 | double dy = y - other.y; 38 | return std::sqrt(dx * dx + dy * dy); 39 | } 40 | 41 | bool Vector2D::inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const { 42 | const auto a = ((p2.y - p3.y) * (x - p3.x) + (p3.x - p2.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); 43 | const auto b = ((p3.y - p1.y) * (x - p3.x) + (p1.x - p3.x) * (y - p3.y)) / ((p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y)); 44 | const auto c = 1 - a - b; 45 | 46 | return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; 47 | } 48 | 49 | double Vector2D::size() const { 50 | return std::sqrt(x * x + y * y); 51 | } 52 | -------------------------------------------------------------------------------- /src/helpers/Vector2D.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "../macros.hpp" 6 | 7 | class Vector2D { 8 | public: 9 | Vector2D(double, double); 10 | Vector2D(); 11 | ~Vector2D(); 12 | 13 | double x = 0; 14 | double y = 0; 15 | 16 | // returns the scale 17 | double normalize(); 18 | 19 | Vector2D operator+(const Vector2D& a) const { 20 | return Vector2D(this->x + a.x, this->y + a.y); 21 | } 22 | Vector2D operator-(const Vector2D& a) const { 23 | return Vector2D(this->x - a.x, this->y - a.y); 24 | } 25 | Vector2D operator-() const { 26 | return Vector2D(-this->x, -this->y); 27 | } 28 | Vector2D operator*(const float& a) const { 29 | return Vector2D(this->x * a, this->y * a); 30 | } 31 | Vector2D operator/(const float& a) const { 32 | return Vector2D(this->x / a, this->y / a); 33 | } 34 | 35 | bool operator==(const Vector2D& a) const { 36 | return a.x == x && a.y == y; 37 | } 38 | 39 | bool operator!=(const Vector2D& a) const { 40 | return a.x != x || a.y != y; 41 | } 42 | 43 | Vector2D operator*(const Vector2D& a) const { 44 | return Vector2D(this->x * a.x, this->y * a.y); 45 | } 46 | 47 | Vector2D operator/(const Vector2D& a) const { 48 | return Vector2D(this->x / a.x, this->y / a.y); 49 | } 50 | 51 | bool operator>(const Vector2D& a) const { 52 | return this->x > a.x && this->y > a.y; 53 | } 54 | 55 | bool operator<(const Vector2D& a) const { 56 | return this->x < a.x && this->y < a.y; 57 | } 58 | 59 | double distance(const Vector2D& other) const; 60 | double size() const; 61 | Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D()) const; 62 | 63 | Vector2D floor() const; 64 | 65 | bool inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const; 66 | }; 67 | 68 | /** 69 | format specification 70 | - 'j', as json array 71 | - 'X', same as std::format("{}x{}", vec.x, vec.y) 72 | - number, floating point precision, use `0` to format as integer 73 | */ 74 | template 75 | struct std::formatter : std::formatter { 76 | bool formatJson = false; 77 | bool formatX = false; 78 | std::string precision = ""; 79 | FORMAT_PARSE(FORMAT_FLAG('j', formatJson) // 80 | FORMAT_FLAG('X', formatX) // 81 | FORMAT_NUMBER(precision), 82 | Vector2D) 83 | 84 | template 85 | auto format(const Vector2D& vec, FormatContext& ctx) const { 86 | std::string formatString = precision.empty() ? "{}" : std::format("{{:.{}f}}", precision); 87 | 88 | if (formatJson) 89 | formatString = std::format("[{0}, {0}]", formatString); 90 | else if (formatX) 91 | formatString = std::format("{0}x{0}", formatString); 92 | else 93 | formatString = std::format("[Vector2D: x: {0}, y: {0}]", formatString); 94 | try { 95 | string buf = std::vformat(formatString, std::make_format_args(vec.x, vec.y)); 96 | return std::format_to(ctx.out(), "{}", buf); 97 | } catch (std::format_error& e) { return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); } 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /src/helpers/WLListener.cpp: -------------------------------------------------------------------------------- 1 | #include "WLListener.hpp" 2 | #include "MiscFunctions.hpp" 3 | #include 4 | #include "../debug/Log.hpp" 5 | #include "Watchdog.hpp" 6 | 7 | void handleWrapped(wl_listener* listener, void* data) { 8 | CHyprWLListener::SWrapper* pWrap = wl_container_of(listener, pWrap, m_sListener); 9 | 10 | g_pWatchdog->startWatching(); 11 | 12 | try { 13 | pWrap->m_pSelf->emit(data); 14 | } catch (std::exception& e) { Debug::log(ERR, "Listener {} timed out and was killed by Watchdog!!!", (uintptr_t)listener); } 15 | 16 | g_pWatchdog->endWatching(); 17 | } 18 | 19 | CHyprWLListener::CHyprWLListener(wl_signal* pSignal, std::function callback, void* pOwner) { 20 | initCallback(pSignal, callback, pOwner); 21 | } 22 | 23 | CHyprWLListener::CHyprWLListener() { 24 | m_swWrapper.m_pSelf = this; 25 | m_swWrapper.m_sListener.notify = &handleWrapped; 26 | wl_list_init(&m_swWrapper.m_sListener.link); 27 | } 28 | 29 | CHyprWLListener::~CHyprWLListener() { 30 | removeCallback(); 31 | } 32 | 33 | void CHyprWLListener::removeCallback() { 34 | if (isConnected()) { 35 | Debug::log(LOG, "Callback {:x} -> {:x}, {} removed.", (uintptr_t)&m_pCallback, (uintptr_t)&m_pOwner, m_szAuthor); 36 | wl_list_remove(&m_swWrapper.m_sListener.link); 37 | wl_list_init(&m_swWrapper.m_sListener.link); 38 | } 39 | } 40 | 41 | bool CHyprWLListener::isConnected() { 42 | return !wl_list_empty(&m_swWrapper.m_sListener.link); 43 | } 44 | 45 | void CHyprWLListener::initCallback(wl_signal* pSignal, std::function callback, void* pOwner, std::string author) { 46 | if (isConnected()) { 47 | Debug::log(ERR, "Tried to connect a listener twice?!"); 48 | return; 49 | } 50 | 51 | m_pOwner = pOwner; 52 | m_pCallback = callback; 53 | m_szAuthor = author; 54 | 55 | addWLSignal(pSignal, &m_swWrapper.m_sListener, pOwner, m_szAuthor); 56 | } 57 | 58 | void CHyprWLListener::emit(void* data) { 59 | m_pCallback(m_pOwner, data); 60 | } 61 | -------------------------------------------------------------------------------- /src/helpers/WLListener.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class CHyprWLListener { 8 | public: 9 | CHyprWLListener(wl_signal*, std::function, void* owner); 10 | CHyprWLListener(); 11 | ~CHyprWLListener(); 12 | 13 | CHyprWLListener(const CHyprWLListener&) = delete; 14 | CHyprWLListener(CHyprWLListener&&) = delete; 15 | CHyprWLListener& operator=(const CHyprWLListener&) = delete; 16 | CHyprWLListener& operator=(CHyprWLListener&&) = delete; 17 | 18 | void initCallback(wl_signal*, std::function, void* owner, std::string author = ""); 19 | 20 | void removeCallback(); 21 | 22 | bool isConnected(); 23 | 24 | struct SWrapper { 25 | wl_listener m_sListener; 26 | CHyprWLListener* m_pSelf; 27 | }; 28 | 29 | void emit(void*); 30 | 31 | private: 32 | SWrapper m_swWrapper; 33 | 34 | void* m_pOwner = nullptr; 35 | 36 | std::function m_pCallback = nullptr; 37 | 38 | std::string m_szAuthor = ""; 39 | }; -------------------------------------------------------------------------------- /src/helpers/WLSurface.cpp: -------------------------------------------------------------------------------- 1 | #include "WLSurface.hpp" 2 | #include "../Compositor.hpp" 3 | 4 | CWLSurface::CWLSurface(wlr_surface* pSurface) { 5 | m_pWLRSurface = pSurface; 6 | init(); 7 | } 8 | 9 | void CWLSurface::assign(wlr_surface* pSurface) { 10 | m_pWLRSurface = pSurface; 11 | init(); 12 | } 13 | 14 | void CWLSurface::unassign() { 15 | destroy(); 16 | } 17 | 18 | CWLSurface::~CWLSurface() { 19 | destroy(); 20 | } 21 | 22 | bool CWLSurface::exists() const { 23 | return m_pWLRSurface; 24 | } 25 | 26 | wlr_surface* CWLSurface::wlr() const { 27 | return m_pWLRSurface; 28 | } 29 | 30 | bool CWLSurface::small() const { 31 | if (!m_pOwner || !exists()) 32 | return false; 33 | 34 | return m_pOwner->m_vReportedSize.x > m_pWLRSurface->current.buffer_width + 1 || m_pOwner->m_vReportedSize.y > m_pWLRSurface->current.buffer_height + 1; 35 | } 36 | 37 | Vector2D CWLSurface::correctSmallVec() const { 38 | if (!m_pOwner || !exists() || !small() || m_bFillIgnoreSmall) 39 | return {}; 40 | 41 | const auto SIZE = getViewporterCorrectedSize(); 42 | 43 | return Vector2D{(m_pOwner->m_vReportedSize.x - SIZE.x) / 2, (m_pOwner->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * 44 | (m_pOwner->m_vRealSize.vec() / m_pOwner->m_vReportedSize); 45 | } 46 | 47 | Vector2D CWLSurface::getViewporterCorrectedSize() const { 48 | if (!exists()) 49 | return {}; 50 | 51 | return m_pWLRSurface->current.viewport.has_dst ? Vector2D{m_pWLRSurface->current.viewport.dst_width, m_pWLRSurface->current.viewport.dst_height} : 52 | Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height}; 53 | } 54 | 55 | void CWLSurface::destroy() { 56 | if (!m_pWLRSurface) 57 | return; 58 | 59 | hyprListener_destroy.removeCallback(); 60 | m_pWLRSurface->data = nullptr; 61 | m_pOwner = nullptr; 62 | 63 | if (g_pCompositor->m_pLastFocus == m_pWLRSurface) 64 | g_pCompositor->m_pLastFocus = nullptr; 65 | if (g_pInputManager->m_pLastMouseSurface == m_pWLRSurface) 66 | g_pInputManager->m_pLastMouseSurface = nullptr; 67 | if (g_pHyprRenderer->m_sLastCursorData.surf == m_pWLRSurface) 68 | g_pInputManager->setCursorImageOverride("left_ptr"); 69 | 70 | m_pWLRSurface = nullptr; 71 | 72 | Debug::log(LOG, "CWLSurface {:x} called destroy()", (uintptr_t)this); 73 | } 74 | 75 | void CWLSurface::init() { 76 | if (!m_pWLRSurface) 77 | return; 78 | 79 | RASSERT(!m_pWLRSurface->data, "Attempted to duplicate CWLSurface ownership!"); 80 | 81 | m_pWLRSurface->data = this; 82 | 83 | hyprListener_destroy.initCallback( 84 | &m_pWLRSurface->events.destroy, [&](void* owner, void* data) { destroy(); }, this, "CWLSurface"); 85 | 86 | Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this); 87 | } -------------------------------------------------------------------------------- /src/helpers/WLSurface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | class CWindow; 6 | 7 | class CWLSurface { 8 | public: 9 | CWLSurface() = default; 10 | CWLSurface(wlr_surface* pSurface); 11 | ~CWLSurface(); 12 | 13 | void assign(wlr_surface* pSurface); 14 | void unassign(); 15 | 16 | CWLSurface(const CWLSurface&) = delete; 17 | CWLSurface(CWLSurface&&) = delete; 18 | CWLSurface& operator=(const CWLSurface&) = delete; 19 | CWLSurface& operator=(CWLSurface&&) = delete; 20 | 21 | wlr_surface* wlr() const; 22 | bool exists() const; 23 | bool small() const; // means surface is smaller than the requested size 24 | Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces 25 | Vector2D getViewporterCorrectedSize() const; 26 | 27 | // allow stretching. Useful for plugins. 28 | bool m_bFillIgnoreSmall = false; 29 | 30 | // if present, means this is a base surface of a window. Cleaned on unassign() 31 | CWindow* m_pOwner = nullptr; 32 | 33 | CWLSurface& operator=(wlr_surface* pSurface) { 34 | destroy(); 35 | m_pWLRSurface = pSurface; 36 | init(); 37 | 38 | return *this; 39 | } 40 | 41 | bool operator==(const CWLSurface& other) const { 42 | return other.wlr() == wlr(); 43 | } 44 | 45 | bool operator==(const wlr_surface* other) const { 46 | return other == wlr(); 47 | } 48 | 49 | explicit operator bool() const { 50 | return exists(); 51 | } 52 | 53 | static CWLSurface* surfaceFromWlr(wlr_surface* pSurface) { 54 | return (CWLSurface*)pSurface->data; 55 | } 56 | 57 | private: 58 | wlr_surface* m_pWLRSurface = nullptr; 59 | 60 | void destroy(); 61 | void init(); 62 | 63 | DYNLISTENER(destroy); 64 | }; -------------------------------------------------------------------------------- /src/helpers/Watchdog.cpp: -------------------------------------------------------------------------------- 1 | #include "Watchdog.hpp" 2 | #include 3 | #include "config/ConfigManager.hpp" 4 | 5 | CWatchdog::~CWatchdog() { 6 | m_bExitThread = true; 7 | m_bNotified = true; 8 | m_cvWatchdogCondition.notify_all(); 9 | m_pWatchdog.reset(); 10 | } 11 | 12 | CWatchdog::CWatchdog() { 13 | m_iMainThreadPID = pthread_self(); 14 | 15 | m_pWatchdog = std::make_unique([this] { 16 | static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; 17 | 18 | while (1337) { 19 | std::unique_lock lk(m_mWatchdogMutex); 20 | 21 | if (!m_bWillWatch) 22 | m_cvWatchdogCondition.wait(lk, [this] { return m_bNotified; }); 23 | else { 24 | if (m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(*PTIMEOUT * 1000.0)), [this] { return m_bNotified; }) == false) 25 | pthread_kill(m_iMainThreadPID, SIGUSR1); 26 | } 27 | 28 | if (m_bExitThread) 29 | break; 30 | 31 | m_bWatching = false; 32 | m_bNotified = false; 33 | } 34 | }); 35 | 36 | m_pWatchdog->detach(); 37 | } 38 | 39 | void CWatchdog::startWatching() { 40 | static auto* const PTIMEOUT = &g_pConfigManager->getConfigValuePtr("debug:watchdog_timeout")->intValue; 41 | 42 | if (*PTIMEOUT == 0) 43 | return; 44 | 45 | m_tTriggered = std::chrono::high_resolution_clock::now(); 46 | m_bWillWatch = true; 47 | m_bWatching = true; 48 | 49 | m_bNotified = true; 50 | m_cvWatchdogCondition.notify_all(); 51 | } 52 | 53 | void CWatchdog::endWatching() { 54 | m_bWatching = false; 55 | m_bWillWatch = false; 56 | 57 | m_bNotified = true; 58 | m_cvWatchdogCondition.notify_all(); 59 | } -------------------------------------------------------------------------------- /src/helpers/Watchdog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class CWatchdog { 9 | public: 10 | // must be called from the main thread 11 | CWatchdog(); 12 | ~CWatchdog(); 13 | 14 | void startWatching(); 15 | void endWatching(); 16 | 17 | private: 18 | std::chrono::high_resolution_clock::time_point m_tTriggered; 19 | 20 | pthread_t m_iMainThreadPID = 0; 21 | 22 | bool m_bWatching = false; 23 | bool m_bWillWatch = false; 24 | 25 | std::unique_ptr m_pWatchdog; 26 | std::mutex m_mWatchdogMutex; 27 | bool m_bNotified = false; 28 | bool m_bExitThread = false; 29 | std::condition_variable m_cvWatchdogCondition; 30 | }; 31 | 32 | inline std::unique_ptr g_pWatchdog; -------------------------------------------------------------------------------- /src/helpers/Workspace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AnimatedVariable.hpp" 4 | #include 5 | #include "../defines.hpp" 6 | 7 | enum eFullscreenMode : int8_t { 8 | FULLSCREEN_INVALID = -1, 9 | FULLSCREEN_FULL = 0, 10 | FULLSCREEN_MAXIMIZED 11 | }; 12 | 13 | class CWindow; 14 | 15 | class CWorkspace { 16 | public: 17 | CWorkspace(int monitorID, std::string name, bool special = false); 18 | ~CWorkspace(); 19 | 20 | // Workspaces ID-based have IDs > 0 21 | // and workspaces name-based have IDs starting with -1337 22 | int m_iID = -1; 23 | std::string m_szName = ""; 24 | uint64_t m_iMonitorID = -1; 25 | // Previous workspace ID is stored during a workspace change, allowing travel 26 | // to the previous workspace. 27 | struct SPrevWorkspaceData { 28 | int iID = -1; 29 | std::string name = ""; 30 | } m_sPrevWorkspace; 31 | 32 | bool m_bHasFullscreenWindow = false; 33 | eFullscreenMode m_efFullscreenMode = FULLSCREEN_FULL; 34 | 35 | wl_array m_wlrCoordinateArr; 36 | 37 | // for animations 38 | CAnimatedVariable m_vRenderOffset; 39 | CAnimatedVariable m_fAlpha; 40 | bool m_bForceRendering = false; 41 | 42 | // "scratchpad" 43 | bool m_bIsSpecialWorkspace = false; 44 | 45 | // last window 46 | CWindow* m_pLastFocusedWindow = nullptr; 47 | 48 | // user-set 49 | bool m_bDefaultFloating = false; 50 | bool m_bDefaultPseudo = false; 51 | 52 | // last monitor (used on reconnect) 53 | std::string m_szLastMonitor = ""; 54 | 55 | // Whether the user configured command for on-created-empty has been executed, if any 56 | bool m_bOnCreatedEmptyExecuted = false; 57 | 58 | void startAnim(bool in, bool left, bool instant = false); 59 | void setActive(bool on); 60 | 61 | void moveToMonitor(const int&); 62 | 63 | CWindow* getLastFocusedWindow(); 64 | void rememberPrevWorkspace(const CWorkspace* prevWorkspace); 65 | 66 | std::string getConfigName(); 67 | }; 68 | -------------------------------------------------------------------------------- /src/helpers/X11Stubs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | inline bool wlr_backend_is_x11(void*) { 4 | return false; 5 | } 6 | 7 | inline void wlr_x11_output_create(void*) {} 8 | -------------------------------------------------------------------------------- /src/hyprerror/HyprError.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "../render/Texture.hpp" 5 | #include "../helpers/AnimatedVariable.hpp" 6 | 7 | #include 8 | 9 | class CHyprError { 10 | public: 11 | CHyprError(); 12 | ~CHyprError(); 13 | 14 | void queueCreate(std::string message, const CColor& color); 15 | void draw(); 16 | void destroy(); 17 | 18 | private: 19 | void createQueued(); 20 | std::string m_szQueued = ""; 21 | CColor m_cQueued; 22 | bool m_bQueuedDestroy = false; 23 | bool m_bIsCreated = false; 24 | CTexture m_tTexture; 25 | CAnimatedVariable m_fFadeOpacity; 26 | CBox m_bDamageBox = {0, 0, 0, 0}; 27 | 28 | bool m_bMonitorChanged = false; 29 | }; 30 | 31 | inline std::unique_ptr g_pHyprError; // This is a full-screen error. Treat it with respect, and there can only be one at a time. -------------------------------------------------------------------------------- /src/init/initHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "initHelpers.hpp" 2 | 3 | bool Init::isSudo() { 4 | return getuid() != geteuid() || !geteuid(); 5 | } 6 | 7 | void Init::gainRealTime() { 8 | const int minPrio = sched_get_priority_min(SCHED_RR); 9 | int old_policy; 10 | struct sched_param param; 11 | 12 | if (pthread_getschedparam(pthread_self(), &old_policy, ¶m)) { 13 | Debug::log(WARN, "Failed to get old pthread scheduling priority"); 14 | return; 15 | } 16 | 17 | param.sched_priority = minPrio; 18 | 19 | if (pthread_setschedparam(pthread_self(), SCHED_RR, ¶m)) { 20 | Debug::log(WARN, "Failed to change process scheduling strategy"); 21 | return; 22 | } 23 | 24 | pthread_atfork(NULL, NULL, []() { 25 | const struct sched_param param = {.sched_priority = 0}; 26 | if (pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m)) 27 | Debug::log(WARN, "Failed to reset process scheduling strategy"); 28 | }); 29 | } -------------------------------------------------------------------------------- /src/init/initHelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | namespace Init { 6 | bool isSudo(); 7 | void gainRealTime(); 8 | }; -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "defines.hpp" 2 | #include "debug/Log.hpp" 3 | #include "Compositor.hpp" 4 | #include "config/ConfigManager.hpp" 5 | #include "init/initHelpers.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void help() { 15 | std::cout << "usage: Hyprland [arg [...]].\n"; 16 | std::cout << "\nArguments:\n"; 17 | std::cout << " --help -h - Show this message again\n"; 18 | std::cout << " --config FILE -c FILE - Specify config file to use\n"; 19 | std::cout << " --i-am-really-stupid - Omits root user privileges check (why would you do that?)\n"; 20 | } 21 | 22 | int main(int argc, char** argv) { 23 | 24 | if (!getenv("XDG_RUNTIME_DIR")) 25 | throwError("XDG_RUNTIME_DIR is not set!"); 26 | 27 | // export HYPRLAND_CMD 28 | std::string cmd = ""; 29 | for (auto i = 0; i < argc; ++i) 30 | cmd += std::string(i == 0 ? "" : " ") + argv[i]; 31 | 32 | setenv("HYPRLAND_CMD", cmd.c_str(), 1); 33 | setenv("XDG_BACKEND", "wayland", 1); 34 | setenv("_JAVA_AWT_WM_NONREPARENTING", "1", 1); 35 | setenv("MOZ_ENABLE_WAYLAND", "1", 1); 36 | setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1); 37 | 38 | // parse some args 39 | std::string configPath; 40 | bool ignoreSudo = false; 41 | 42 | std::vector args{argv + 1, argv + argc}; 43 | 44 | for (auto it = args.begin(); it != args.end(); it++) { 45 | if (it->compare("--i-am-really-stupid") == 0 && !ignoreSudo) { 46 | std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n"; 47 | 48 | ignoreSudo = true; 49 | } else if (it->compare("-c") == 0 || it->compare("--config") == 0) { 50 | if (std::next(it) == args.end()) { 51 | help(); 52 | 53 | return 1; 54 | } 55 | std::string next_arg = std::next(it)->c_str(); 56 | 57 | if (!std::filesystem::exists(next_arg)) { 58 | std::cerr << "[ ERROR ] Config path '" << next_arg << "' doesn't exist!\n"; 59 | help(); 60 | 61 | return 1; 62 | } 63 | 64 | configPath = next_arg; 65 | Debug::log(LOG, "User-specified config location: '{}'", configPath); 66 | 67 | it++; 68 | 69 | continue; 70 | } else if (it->compare("-h") == 0 || it->compare("--help") == 0) { 71 | help(); 72 | 73 | return 0; 74 | } else { 75 | std::cerr << "[ ERROR ] Unknown option '" << it->c_str() << "'!\n"; 76 | help(); 77 | 78 | return 1; 79 | } 80 | } 81 | 82 | if (!ignoreSudo && Init::isSudo()) { 83 | std::cerr << "[ ERROR ] Hyprland was launched with superuser priveleges, but the privileges check is not omitted.\n"; 84 | std::cerr << " Hint: Use the --i-am-really-stupid flag to omit that check.\n"; 85 | 86 | return 1; 87 | } else if (ignoreSudo && Init::isSudo()) { 88 | std::cout << "Superuser privileges check is omitted. I hope you know what you're doing.\n"; 89 | } 90 | 91 | std::cout << "Welcome to Hyprland!\n"; 92 | 93 | // let's init the compositor. 94 | // it initializes basic Wayland stuff in the constructor. 95 | g_pCompositor = std::make_unique(); 96 | g_pCompositor->explicitConfigPath = configPath; 97 | 98 | g_pCompositor->initServer(); 99 | 100 | Init::gainRealTime(); 101 | 102 | Debug::log(LOG, "Hyprland init finished."); 103 | 104 | // If all's good to go, start. 105 | g_pCompositor->startCompositor(); 106 | 107 | // If we are here it means we got yote. 108 | Debug::log(LOG, "Hyprland reached the end."); 109 | g_pCompositor.reset(); 110 | 111 | return EXIT_SUCCESS; 112 | } 113 | -------------------------------------------------------------------------------- /src/managers/AnimationManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include 5 | #include 6 | #include "../helpers/AnimatedVariable.hpp" 7 | #include "../helpers/BezierCurve.hpp" 8 | #include "../Window.hpp" 9 | #include "../helpers/Timer.hpp" 10 | 11 | class CAnimationManager { 12 | public: 13 | CAnimationManager(); 14 | 15 | void tick(); 16 | bool shouldTickForNext(); 17 | void onTicked(); 18 | void scheduleTick(); 19 | void addBezierWithName(std::string, const Vector2D&, const Vector2D&); 20 | void removeAllBeziers(); 21 | 22 | void onWindowPostCreateClose(CWindow*, bool close = false); 23 | 24 | bool bezierExists(const std::string&); 25 | CBezierCurve* getBezier(const std::string&); 26 | 27 | std::string styleValidInConfigVar(const std::string&, const std::string&); 28 | 29 | std::unordered_map getAllBeziers(); 30 | 31 | std::vector m_vAnimatedVariables; 32 | std::vector m_vActiveAnimatedVariables; 33 | 34 | wl_event_source* m_pAnimationTick; 35 | 36 | float m_fLastTickTime; // in ms 37 | 38 | private: 39 | bool deltaSmallToFlip(const Vector2D& a, const Vector2D& b); 40 | bool deltaSmallToFlip(const CColor& a, const CColor& b); 41 | bool deltaSmallToFlip(const float& a, const float& b); 42 | bool deltazero(const Vector2D& a, const Vector2D& b); 43 | bool deltazero(const CColor& a, const CColor& b); 44 | bool deltazero(const float& a, const float& b); 45 | 46 | std::unordered_map m_mBezierCurves; 47 | 48 | bool m_bTickScheduled = false; 49 | 50 | // Anim stuff 51 | void animationPopin(CWindow*, bool close = false, float minPerc = 0.f); 52 | void animationSlide(CWindow*, std::string force = "", bool close = false); 53 | }; 54 | 55 | inline std::unique_ptr g_pAnimationManager; -------------------------------------------------------------------------------- /src/managers/EventManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../defines.hpp" 7 | #include "../helpers/MiscFunctions.hpp" 8 | 9 | struct SHyprIPCEvent { 10 | std::string event; 11 | std::string data; 12 | }; 13 | 14 | class CEventManager { 15 | public: 16 | CEventManager(); 17 | 18 | void postEvent(const SHyprIPCEvent event); 19 | 20 | void startThread(); 21 | 22 | std::thread m_tThread; 23 | private: 24 | void flushEvents(); 25 | 26 | std::mutex eventQueueMutex; 27 | std::deque m_dQueuedEvents; 28 | 29 | std::deque> m_dAcceptedSocketFDs; 30 | }; 31 | 32 | inline std::unique_ptr g_pEventManager; 33 | -------------------------------------------------------------------------------- /src/managers/HookSystemManager.cpp: -------------------------------------------------------------------------------- 1 | #include "HookSystemManager.hpp" 2 | 3 | #include "../plugins/PluginSystem.hpp" 4 | 5 | CHookSystemManager::CHookSystemManager() { 6 | ; // 7 | } 8 | 9 | // returns the pointer to the function 10 | HOOK_CALLBACK_FN* CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle) { 11 | const auto PVEC = getVecForEvent(event); 12 | const auto PFN = &m_lCallbackFunctions.emplace_back(fn); 13 | PVEC->emplace_back(SCallbackFNPtr{PFN, handle}); 14 | return PFN; 15 | } 16 | 17 | void CHookSystemManager::hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle) { 18 | const auto PVEC = getVecForEvent(event); 19 | PVEC->emplace_back(SCallbackFNPtr{fn, handle}); 20 | } 21 | 22 | void CHookSystemManager::unhook(HOOK_CALLBACK_FN* fn) { 23 | std::erase_if(m_lCallbackFunctions, [&](const auto& other) { return &other == fn; }); 24 | for (auto& [k, v] : m_lpRegisteredHooks) { 25 | std::erase_if(v, [&](const auto& other) { return other.fn == fn; }); 26 | } 27 | } 28 | 29 | void CHookSystemManager::emit(const std::vector* callbacks, SCallbackInfo& info, std::any data) { 30 | if (callbacks->empty()) 31 | return; 32 | 33 | std::vector faultyHandles; 34 | 35 | for (auto& cb : *callbacks) { 36 | 37 | m_bCurrentEventPlugin = false; 38 | 39 | if (!cb.handle) { 40 | // we don't guard hl hooks 41 | (*cb.fn)(cb.fn, info, data); 42 | continue; 43 | } 44 | 45 | m_bCurrentEventPlugin = true; 46 | 47 | if (std::find(faultyHandles.begin(), faultyHandles.end(), cb.handle) != faultyHandles.end()) 48 | continue; 49 | 50 | try { 51 | if (!setjmp(m_jbHookFaultJumpBuf)) 52 | (*cb.fn)(cb.fn, info, data); 53 | else { 54 | // this module crashed. 55 | throw std::exception(); 56 | } 57 | } catch (std::exception& e) { 58 | // TODO: this works only once...? 59 | faultyHandles.push_back(cb.handle); 60 | Debug::log(ERR, "[hookSystem] Hook from plugin {:x} caused a SIGSEGV, queueing for unloading.", (uintptr_t)cb.handle); 61 | } 62 | } 63 | 64 | if (!faultyHandles.empty()) { 65 | for (auto& h : faultyHandles) 66 | g_pPluginSystem->unloadPlugin(g_pPluginSystem->getPluginByHandle(h), true); 67 | } 68 | } 69 | 70 | std::vector* CHookSystemManager::getVecForEvent(const std::string& event) { 71 | auto IT = std::find_if(m_lpRegisteredHooks.begin(), m_lpRegisteredHooks.end(), [&](const auto& other) { return other.first == event; }); 72 | 73 | if (IT != m_lpRegisteredHooks.end()) 74 | return &IT->second; 75 | 76 | Debug::log(LOG, "[hookSystem] New hook event registered: {}", event); 77 | 78 | return &m_lpRegisteredHooks.emplace_back(std::make_pair<>(event, std::vector{})).second; 79 | } -------------------------------------------------------------------------------- /src/managers/HookSystemManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "../plugins/PluginAPI.hpp" 13 | 14 | // global typedef for hooked functions. Passes itself as a ptr when called, and `data` additionally. 15 | 16 | typedef std::function HOOK_CALLBACK_FN; 17 | 18 | struct SCallbackFNPtr { 19 | HOOK_CALLBACK_FN* fn = nullptr; 20 | HANDLE handle = nullptr; 21 | }; 22 | 23 | #define EMIT_HOOK_EVENT(name, param) \ 24 | { \ 25 | static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \ 26 | SCallbackInfo info; \ 27 | g_pHookSystem->emit(PEVENTVEC, info, param); \ 28 | } 29 | 30 | #define EMIT_HOOK_EVENT_CANCELLABLE(name, param) \ 31 | { \ 32 | static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \ 33 | SCallbackInfo info; \ 34 | g_pHookSystem->emit(PEVENTVEC, info, param); \ 35 | if (info.cancelled) \ 36 | return; \ 37 | } 38 | 39 | class CHookSystemManager { 40 | public: 41 | CHookSystemManager(); 42 | 43 | // returns the pointer to the function 44 | HOOK_CALLBACK_FN* hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle = nullptr); 45 | void hookStatic(const std::string& event, HOOK_CALLBACK_FN* fn, HANDLE handle = nullptr); 46 | void unhook(HOOK_CALLBACK_FN* fn); 47 | 48 | void emit(const std::vector* callbacks, SCallbackInfo& info, std::any data = 0); 49 | std::vector* getVecForEvent(const std::string& event); 50 | 51 | bool m_bCurrentEventPlugin = false; 52 | jmp_buf m_jbHookFaultJumpBuf; 53 | 54 | private: 55 | // todo: this is slow. Maybe static ptrs should be somehow allowed. unique ptr for vec? 56 | std::list>> m_lpRegisteredHooks; 57 | std::list m_lCallbackFunctions; 58 | }; 59 | 60 | inline std::unique_ptr g_pHookSystem; -------------------------------------------------------------------------------- /src/managers/LayoutManager.cpp: -------------------------------------------------------------------------------- 1 | #include "LayoutManager.hpp" 2 | 3 | CLayoutManager::CLayoutManager() { 4 | m_vLayouts.emplace_back(std::make_pair<>("dwindle", &m_cDwindleLayout)); 5 | m_vLayouts.emplace_back(std::make_pair<>("master", &m_cMasterLayout)); 6 | } 7 | 8 | IHyprLayout* CLayoutManager::getCurrentLayout() { 9 | return m_vLayouts[m_iCurrentLayoutID].second; 10 | } 11 | 12 | void CLayoutManager::switchToLayout(std::string layout) { 13 | for (size_t i = 0; i < m_vLayouts.size(); ++i) { 14 | if (m_vLayouts[i].first == layout) { 15 | if (i == (size_t)m_iCurrentLayoutID) 16 | return; 17 | 18 | getCurrentLayout()->onDisable(); 19 | m_iCurrentLayoutID = i; 20 | getCurrentLayout()->onEnable(); 21 | return; 22 | } 23 | } 24 | 25 | Debug::log(ERR, "Unknown layout!"); 26 | } 27 | 28 | bool CLayoutManager::addLayout(const std::string& name, IHyprLayout* layout) { 29 | if (std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.first == name || other.second == layout; }) != m_vLayouts.end()) 30 | return false; 31 | 32 | m_vLayouts.emplace_back(std::make_pair<>(name, layout)); 33 | 34 | Debug::log(LOG, "Added new layout {} at {:x}", name, (uintptr_t)layout); 35 | 36 | return true; 37 | } 38 | 39 | bool CLayoutManager::removeLayout(IHyprLayout* layout) { 40 | const auto IT = std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.second == layout; }); 41 | 42 | if (IT == m_vLayouts.end() || IT->first == "dwindle" || IT->first == "master") 43 | return false; 44 | 45 | if (m_iCurrentLayoutID == IT - m_vLayouts.begin()) 46 | switchToLayout("dwindle"); 47 | 48 | Debug::log(LOG, "Removed a layout {} at {:x}", IT->first, (uintptr_t)layout); 49 | 50 | std::erase(m_vLayouts, *IT); 51 | 52 | return true; 53 | } 54 | -------------------------------------------------------------------------------- /src/managers/LayoutManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../layout/DwindleLayout.hpp" 4 | #include "../layout/MasterLayout.hpp" 5 | 6 | class CLayoutManager { 7 | public: 8 | CLayoutManager(); 9 | 10 | IHyprLayout* getCurrentLayout(); 11 | 12 | void switchToLayout(std::string); 13 | 14 | bool addLayout(const std::string& name, IHyprLayout* layout); 15 | bool removeLayout(IHyprLayout* layout); 16 | 17 | private: 18 | enum HYPRLAYOUTS 19 | { 20 | LAYOUT_DWINDLE = 0, 21 | LAYOUT_MASTER 22 | }; 23 | 24 | int m_iCurrentLayoutID = LAYOUT_DWINDLE; 25 | 26 | CHyprDwindleLayout m_cDwindleLayout; 27 | CHyprMasterLayout m_cMasterLayout; 28 | 29 | std::vector> m_vLayouts; 30 | }; 31 | 32 | inline std::unique_ptr g_pLayoutManager; -------------------------------------------------------------------------------- /src/managers/ProtocolManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ProtocolManager.hpp" 2 | 3 | #include "xdg-output-unstable-v1-protocol.h" 4 | 5 | CProtocolManager::CProtocolManager() { 6 | m_pToplevelExportProtocolManager = std::make_unique(); 7 | m_pFractionalScaleProtocolManager = std::make_unique(); 8 | m_pTextInputV1ProtocolManager = std::make_unique(); 9 | m_pGlobalShortcutsProtocolManager = std::make_unique(); 10 | m_pScreencopyProtocolManager = std::make_unique(); 11 | 12 | m_pXDGOutputProtocol = std::make_unique(&zxdg_output_manager_v1_interface, 3, "XDGOutput"); 13 | } 14 | -------------------------------------------------------------------------------- /src/managers/ProtocolManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "../protocols/ToplevelExport.hpp" 5 | #include "../protocols/FractionalScale.hpp" 6 | #include "../protocols/TextInputV1.hpp" 7 | #include "../protocols/GlobalShortcuts.hpp" 8 | #include "../protocols/Screencopy.hpp" 9 | #include "../protocols/XDGOutput.hpp" 10 | 11 | class CProtocolManager { 12 | public: 13 | CProtocolManager(); 14 | 15 | // TODO: rewrite to use the new protocol framework 16 | std::unique_ptr m_pToplevelExportProtocolManager; 17 | std::unique_ptr m_pFractionalScaleProtocolManager; 18 | std::unique_ptr m_pTextInputV1ProtocolManager; 19 | std::unique_ptr m_pGlobalShortcutsProtocolManager; 20 | std::unique_ptr m_pScreencopyProtocolManager; 21 | 22 | // New protocols 23 | std::unique_ptr m_pXDGOutputProtocol; 24 | }; 25 | 26 | inline std::unique_ptr g_pProtocolManager; 27 | -------------------------------------------------------------------------------- /src/managers/SessionLockManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | struct SSessionLockSurface { 6 | wlr_session_lock_surface_v1* pWlrLockSurface = nullptr; 7 | int iMonitorID = -1; 8 | 9 | bool mapped = false; 10 | 11 | DYNLISTENER(map); 12 | DYNLISTENER(destroy); 13 | DYNLISTENER(commit); 14 | }; 15 | 16 | struct SSessionLock { 17 | bool active = false; 18 | wlr_session_lock_v1* pWlrLock = nullptr; 19 | 20 | std::vector> vSessionLockSurfaces; 21 | 22 | DYNLISTENER(newSurface); 23 | DYNLISTENER(unlock); 24 | DYNLISTENER(destroy); 25 | }; 26 | 27 | class CSessionLockManager { 28 | public: 29 | CSessionLockManager() = default; 30 | ~CSessionLockManager() = default; 31 | 32 | void onNewSessionLock(wlr_session_lock_v1*); 33 | SSessionLockSurface* getSessionLockSurfaceForMonitor(const int&); 34 | 35 | bool isSessionLocked(); 36 | bool isSurfaceSessionLock(wlr_surface*); 37 | 38 | void removeSessionLockSurface(SSessionLockSurface*); 39 | 40 | void activateLock(); 41 | 42 | private: 43 | SSessionLock m_sSessionLock; 44 | }; 45 | 46 | inline std::unique_ptr g_pSessionLockManager; -------------------------------------------------------------------------------- /src/managers/ThreadManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ThreadManager.hpp" 2 | #include "../debug/HyprCtl.hpp" 3 | #include "../Compositor.hpp" 4 | 5 | int slowUpdate = 0; 6 | 7 | int handleTimer(void* data) { 8 | const auto PTM = (CThreadManager*)data; 9 | 10 | static auto* const PDISABLECFGRELOAD = &g_pConfigManager->getConfigValuePtr("misc:disable_autoreload")->intValue; 11 | 12 | if (*PDISABLECFGRELOAD != 1) 13 | g_pConfigManager->tick(); 14 | 15 | wl_event_source_timer_update(PTM->m_esConfigTimer, 1000); 16 | 17 | return 0; 18 | } 19 | 20 | CThreadManager::CThreadManager() { 21 | HyprCtl::startHyprCtlSocket(); 22 | 23 | m_esConfigTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, handleTimer, this); 24 | 25 | wl_event_source_timer_update(m_esConfigTimer, 1000); 26 | } 27 | 28 | CThreadManager::~CThreadManager() { 29 | // 30 | } -------------------------------------------------------------------------------- /src/managers/ThreadManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include 5 | #include "../Compositor.hpp" 6 | 7 | class CThreadManager { 8 | public: 9 | CThreadManager(); 10 | ~CThreadManager(); 11 | 12 | wl_event_source* m_esConfigTimer; 13 | 14 | private: 15 | }; 16 | 17 | inline std::unique_ptr g_pThreadManager; -------------------------------------------------------------------------------- /src/managers/XWaylandManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include 5 | 6 | class CWindow; // because clangd 7 | 8 | class CHyprXWaylandManager { 9 | public: 10 | CHyprXWaylandManager(); 11 | ~CHyprXWaylandManager(); 12 | 13 | wlr_xwayland* m_sWLRXWayland = nullptr; 14 | 15 | wlr_surface* getWindowSurface(CWindow*); 16 | void activateSurface(wlr_surface*, bool); 17 | void activateWindow(CWindow*, bool); 18 | void getGeometryForWindow(CWindow*, CBox*); 19 | std::string getTitle(CWindow*); 20 | std::string getAppIDClass(CWindow*); 21 | void sendCloseWindow(CWindow*); 22 | void setWindowSize(CWindow*, Vector2D, bool force = false); 23 | void setWindowStyleTiled(CWindow*, uint32_t); 24 | void setWindowFullscreen(CWindow*, bool); 25 | wlr_surface* surfaceAt(CWindow*, const Vector2D&, Vector2D&); 26 | bool shouldBeFloated(CWindow*); 27 | void moveXWaylandWindow(CWindow*, const Vector2D&); 28 | void checkBorders(CWindow*); 29 | Vector2D getMaxSizeForWindow(CWindow*); 30 | Vector2D xwaylandToWaylandCoords(const Vector2D&); 31 | }; 32 | 33 | inline std::unique_ptr g_pXWaylandManager; -------------------------------------------------------------------------------- /src/managers/input/IdleInhibitor.cpp: -------------------------------------------------------------------------------- 1 | #include "InputManager.hpp" 2 | #include "../../Compositor.hpp" 3 | 4 | void Events::listener_newIdleInhibitor(wl_listener* listener, void* data) { 5 | const auto WLRIDLEINHIBITOR = (wlr_idle_inhibitor_v1*)data; 6 | 7 | if (!WLRIDLEINHIBITOR) 8 | return; 9 | 10 | g_pInputManager->newIdleInhibitor(WLRIDLEINHIBITOR); 11 | } 12 | 13 | void CInputManager::newIdleInhibitor(wlr_idle_inhibitor_v1* pInhibitor) { 14 | const auto PINHIBIT = &m_lIdleInhibitors.emplace_back(); 15 | 16 | Debug::log(LOG, "New idle inhibitor registered"); 17 | 18 | PINHIBIT->pWlrInhibitor = pInhibitor; 19 | 20 | PINHIBIT->hyprListener_Destroy.initCallback( 21 | &pInhibitor->events.destroy, 22 | [](void* owner, void* data) { 23 | const auto PINH = (SIdleInhibitor*)owner; 24 | 25 | g_pInputManager->m_lIdleInhibitors.remove(*PINH); 26 | 27 | Debug::log(LOG, "Destroyed an idleinhibitor"); 28 | 29 | g_pInputManager->recheckIdleInhibitorStatus(); 30 | }, 31 | PINHIBIT, "IdleInhibitor"); 32 | 33 | PINHIBIT->pWindow = g_pCompositor->getWindowFromSurface(pInhibitor->surface); 34 | 35 | if (PINHIBIT->pWindow) 36 | Debug::log(LOG, "IdleInhibitor got window {}", PINHIBIT->pWindow); 37 | 38 | recheckIdleInhibitorStatus(); 39 | } 40 | 41 | void CInputManager::recheckIdleInhibitorStatus() { 42 | 43 | for (auto& ii : m_lIdleInhibitors) { 44 | if (!ii.pWindow) { 45 | g_pCompositor->setIdleActivityInhibit(false); 46 | return; 47 | } else if (g_pHyprRenderer->shouldRenderWindow(ii.pWindow)) { 48 | g_pCompositor->setIdleActivityInhibit(false); 49 | return; 50 | } 51 | } 52 | 53 | // check manual user-set inhibitors 54 | for (auto& w : g_pCompositor->m_vWindows) { 55 | if (w->m_eIdleInhibitMode == IDLEINHIBIT_NONE) 56 | continue; 57 | 58 | if (w->m_eIdleInhibitMode == IDLEINHIBIT_ALWAYS) { 59 | g_pCompositor->setIdleActivityInhibit(false); 60 | return; 61 | } 62 | 63 | if (w->m_eIdleInhibitMode == IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w.get())) { 64 | g_pCompositor->setIdleActivityInhibit(false); 65 | return; 66 | } 67 | 68 | if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->m_bIsFullscreen && g_pCompositor->isWorkspaceVisible(w->m_iWorkspaceID)) { 69 | g_pCompositor->setIdleActivityInhibit(false); 70 | return; 71 | } 72 | } 73 | 74 | g_pCompositor->setIdleActivityInhibit(true); 75 | return; 76 | } -------------------------------------------------------------------------------- /src/managers/input/InputMethodRelay.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../defines.hpp" 4 | #include "../../helpers/WLClasses.hpp" 5 | 6 | class CInputManager; 7 | struct STextInputV1; 8 | 9 | class CInputMethodRelay { 10 | public: 11 | CInputMethodRelay(); 12 | 13 | void onNewIME(wlr_input_method_v2*); 14 | void onNewTextInput(wlr_text_input_v3*); 15 | 16 | wlr_input_method_v2* m_pWLRIME = nullptr; 17 | 18 | void commitIMEState(STextInput* pInput); 19 | void removeTextInput(STextInput* pInput); 20 | 21 | void onKeyboardFocus(wlr_surface*); 22 | 23 | STextInput* getFocusedTextInput(); 24 | STextInput* getFocusableTextInput(); 25 | 26 | void setPendingSurface(STextInput*, wlr_surface*); 27 | 28 | SIMEKbGrab* getIMEKeyboardGrab(SKeyboard*); 29 | 30 | void setIMEPopupFocus(SIMEPopup*, wlr_surface*); 31 | void updateInputPopup(SIMEPopup*); 32 | void damagePopup(SIMEPopup*); 33 | void removePopup(SIMEPopup*); 34 | 35 | private: 36 | std::unique_ptr m_pKeyboardGrab; 37 | 38 | std::list m_lTextInputs; 39 | std::list m_lIMEPopups; 40 | 41 | DYNLISTENER(textInputNew); 42 | DYNLISTENER(IMECommit); 43 | DYNLISTENER(IMEDestroy); 44 | DYNLISTENER(IMEGrab); 45 | DYNLISTENER(IMENewPopup); 46 | 47 | void createNewTextInput(wlr_text_input_v3*, STextInputV1* tiv1 = nullptr); 48 | wlr_surface* focusedSurface(STextInput* pInput); 49 | 50 | friend class CHyprRenderer; 51 | friend class CInputManager; 52 | friend class CTextInputV1ProtocolManager; 53 | }; -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true) 2 | src = globber.stdout().strip().split('\n') 3 | 4 | executable('Hyprland', src, 5 | cpp_args: ['-DWLR_USE_UNSTABLE'], 6 | link_args: '-rdynamic', 7 | cpp_pch: 'pch/pch.hpp', 8 | dependencies: [ 9 | server_protos, 10 | dependency('wayland-server'), 11 | dependency('wayland-client'), 12 | wlroots.get_variable('wlroots'), 13 | dependency('cairo'), 14 | dependency('libdrm'), 15 | dependency('egl'), 16 | dependency('xkbcommon'), 17 | dependency('libinput'), 18 | xcb_dep, 19 | backtrace_dep, 20 | systemd_dep, 21 | udis86, 22 | 23 | dependency('pixman-1'), 24 | dependency('gl', 'opengl'), 25 | dependency('threads'), 26 | dependency('pango'), 27 | dependency('pangocairo') 28 | ], 29 | install : true 30 | ) 31 | -------------------------------------------------------------------------------- /src/pch/pch.hpp: -------------------------------------------------------------------------------- 1 | #include "Compositor.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include -------------------------------------------------------------------------------- /src/plugins/HookSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define HANDLE void* 8 | 9 | class CFunctionHook { 10 | public: 11 | CFunctionHook(HANDLE owner, void* source, void* destination); 12 | ~CFunctionHook(); 13 | 14 | bool hook(); 15 | bool unhook(); 16 | 17 | CFunctionHook(const CFunctionHook&) = delete; 18 | CFunctionHook(CFunctionHook&&) = delete; 19 | CFunctionHook& operator=(const CFunctionHook&) = delete; 20 | CFunctionHook& operator=(CFunctionHook&&) = delete; 21 | 22 | void* m_pOriginal = nullptr; 23 | 24 | private: 25 | void* m_pSource = nullptr; 26 | void* m_pFunctionAddr = nullptr; 27 | void* m_pTrampolineAddr = nullptr; 28 | void* m_pDestination = nullptr; 29 | size_t m_iHookLen = 0; 30 | size_t m_iTrampoLen = 0; 31 | HANDLE m_pOwner = nullptr; 32 | bool m_bActive = false; 33 | 34 | std::vector> m_vTrampolineRIPUses; 35 | 36 | void* m_pOriginalBytes = nullptr; 37 | 38 | size_t probeMinimumJumpSize(void* start, size_t min); 39 | size_t getInstructionLenAt(void* start); 40 | 41 | friend class CHookSystem; 42 | }; 43 | 44 | class CHookSystem { 45 | public: 46 | CFunctionHook* initHook(HANDLE handle, void* source, void* destination); 47 | bool removeHook(CFunctionHook* hook); 48 | 49 | void removeAllHooksFrom(HANDLE handle); 50 | 51 | private: 52 | std::vector> m_vHooks; 53 | }; 54 | 55 | inline std::unique_ptr g_pFunctionHookSystem; -------------------------------------------------------------------------------- /src/plugins/PluginSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "PluginAPI.hpp" 5 | #include 6 | 7 | class IHyprWindowDecoration; 8 | 9 | class CPlugin { 10 | public: 11 | std::string name = ""; 12 | std::string description = ""; 13 | std::string author = ""; 14 | std::string version = ""; 15 | 16 | std::string path = ""; 17 | 18 | bool m_bLoadedWithConfig = false; 19 | 20 | HANDLE m_pHandle = nullptr; 21 | 22 | std::vector registeredLayouts; 23 | std::vector registeredDecorations; 24 | std::vector> registeredCallbacks; 25 | std::vector registeredDispatchers; 26 | }; 27 | 28 | class CPluginSystem { 29 | public: 30 | CPluginSystem(); 31 | 32 | CPlugin* loadPlugin(const std::string& path); 33 | void unloadPlugin(const CPlugin* plugin, bool eject = false); 34 | void unloadAllPlugins(); 35 | std::vector updateConfigPlugins(const std::vector& plugins, bool& changed); 36 | CPlugin* getPluginByPath(const std::string& path); 37 | CPlugin* getPluginByHandle(HANDLE handle); 38 | std::vector getAllPlugins(); 39 | 40 | bool m_bAllowConfigVars = false; 41 | 42 | private: 43 | std::vector> m_vLoadedPlugins; 44 | 45 | jmp_buf m_jbPluginFaultJumpBuf; 46 | }; 47 | 48 | inline std::unique_ptr g_pPluginSystem; 49 | -------------------------------------------------------------------------------- /src/protocols/FractionalScale.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "fractional-scale-v1-protocol.h" 5 | 6 | #include 7 | #include 8 | 9 | struct SFractionalScaleAddon { 10 | wlr_surface* pSurface = nullptr; 11 | double preferredScale = 1.0; 12 | wl_resource* pResource = nullptr; 13 | }; 14 | 15 | class CFractionalScaleProtocolManager { 16 | public: 17 | CFractionalScaleProtocolManager(); 18 | 19 | void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); 20 | 21 | void displayDestroy(); 22 | 23 | void setPreferredScaleForSurface(wlr_surface*, double); 24 | 25 | void removeAddon(wlr_surface*); 26 | 27 | // handlers 28 | 29 | void getFractionalScale(wl_client* client, wl_resource* resource, uint32_t id, wl_resource* surface); 30 | 31 | private: 32 | SFractionalScaleAddon* getAddonForSurface(wlr_surface*); 33 | 34 | std::vector> m_vFractionalScaleAddons; 35 | 36 | wl_global* m_pGlobal = nullptr; 37 | wl_listener m_liDisplayDestroy; 38 | }; -------------------------------------------------------------------------------- /src/protocols/GlobalShortcuts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../defines.hpp" 3 | #include "hyprland-global-shortcuts-v1-protocol.h" 4 | #include 5 | 6 | struct SShortcut { 7 | wl_resource* resource; 8 | std::string id, description, appid; 9 | uint32_t shortcut = 0; 10 | }; 11 | 12 | struct SShortcutClient { 13 | wl_client* client = nullptr; 14 | std::vector> shortcuts; 15 | }; 16 | 17 | class CGlobalShortcutsProtocolManager { 18 | public: 19 | CGlobalShortcutsProtocolManager(); 20 | void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); 21 | void displayDestroy(); 22 | 23 | void registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description, 24 | const char* trigger_description); 25 | void destroyShortcut(wl_resource* resource); 26 | 27 | bool globalShortcutExists(std::string appid, std::string trigger); 28 | void sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed); 29 | 30 | std::vector getAllShortcuts(); 31 | 32 | private: 33 | std::vector> m_vClients; 34 | 35 | SShortcutClient* clientFromWlClient(wl_client* client); 36 | 37 | wl_global* m_pGlobal = nullptr; 38 | wl_listener m_liDisplayDestroy; 39 | }; -------------------------------------------------------------------------------- /src/protocols/Screencopy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "wlr-screencopy-unstable-v1-protocol.h" 5 | 6 | #include 7 | #include 8 | #include "../managers/HookSystemManager.hpp" 9 | #include "../helpers/Timer.hpp" 10 | 11 | class CMonitor; 12 | 13 | enum eClientOwners 14 | { 15 | CLIENT_SCREENCOPY = 0, 16 | CLIENT_TOPLEVEL_EXPORT 17 | }; 18 | 19 | class CScreencopyClient { 20 | public: 21 | CScreencopyClient(); 22 | ~CScreencopyClient(); 23 | 24 | int ref = 0; 25 | wl_resource* resource = nullptr; 26 | 27 | eClientOwners clientOwner = CLIENT_SCREENCOPY; 28 | 29 | int frameCounter = 0; 30 | int framesInLastHalfSecond = 0; 31 | CTimer lastMeasure; 32 | CTimer lastFrame; 33 | bool sentScreencast = false; 34 | 35 | void onTick(); 36 | HOOK_CALLBACK_FN* tickCallback = nullptr; 37 | 38 | bool operator==(const CScreencopyClient& other) const { 39 | return resource == other.resource; 40 | } 41 | }; 42 | 43 | struct SScreencopyFrame { 44 | wl_resource* resource = nullptr; 45 | CScreencopyClient* client = nullptr; 46 | 47 | uint32_t shmFormat = 0; 48 | uint32_t dmabufFormat = 0; 49 | CBox box = {}; 50 | int shmStride = 0; 51 | 52 | bool overlayCursor = false; 53 | bool withDamage = false; 54 | 55 | wlr_buffer_cap bufferCap = WLR_BUFFER_CAP_SHM; 56 | 57 | wlr_buffer* buffer = nullptr; 58 | 59 | CMonitor* pMonitor = nullptr; 60 | CWindow* pWindow = nullptr; 61 | 62 | bool operator==(const SScreencopyFrame& other) const { 63 | return resource == other.resource && client == other.client; 64 | } 65 | }; 66 | 67 | class CScreencopyProtocolManager { 68 | public: 69 | CScreencopyProtocolManager(); 70 | 71 | void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); 72 | void removeClient(CScreencopyClient* client, bool force = false); 73 | void removeFrame(SScreencopyFrame* frame, bool force = false); 74 | void displayDestroy(); 75 | 76 | void captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, CBox box = {0, 0, 0, 0}); 77 | 78 | void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer); 79 | 80 | void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e); 81 | 82 | private: 83 | wl_global* m_pGlobal = nullptr; 84 | std::list m_lFrames; 85 | std::list m_lClients; 86 | 87 | wl_listener m_liDisplayDestroy; 88 | 89 | std::vector m_vFramesAwaitingWrite; 90 | 91 | wlr_buffer* m_pLastMonitorBackBuffer = nullptr; 92 | 93 | void shareAllFrames(CMonitor* pMonitor); 94 | void shareFrame(SScreencopyFrame* frame); 95 | void sendFrameDamage(SScreencopyFrame* frame); 96 | bool copyFrameDmabuf(SScreencopyFrame* frame); 97 | bool copyFrameShm(SScreencopyFrame* frame, timespec* now); 98 | 99 | friend class CScreencopyClient; 100 | }; -------------------------------------------------------------------------------- /src/protocols/TextInputV1.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "text-input-unstable-v1-protocol.h" 5 | 6 | #include 7 | 8 | struct STextInput; 9 | 10 | struct STextInputV1 { 11 | wl_client* client = nullptr; 12 | wl_resource* resourceCaller = nullptr; 13 | 14 | wl_resource* resourceImpl = nullptr; 15 | 16 | wlr_surface* focusedSurface = nullptr; 17 | 18 | STextInput* pTextInput = nullptr; 19 | 20 | wl_signal sEnable; 21 | wl_signal sDisable; 22 | wl_signal sCommit; 23 | wl_signal sDestroy; 24 | 25 | uint32_t serial = 0; 26 | 27 | bool active = false; 28 | 29 | struct SPendingSurr { 30 | bool isPending = false; 31 | std::string text = ""; 32 | uint32_t cursor = 0; 33 | uint32_t anchor = 0; 34 | } pendingSurrounding; 35 | 36 | struct SPendingCT { 37 | bool isPending = false; 38 | uint32_t hint = 0; 39 | uint32_t purpose = 0; 40 | } pendingContentType; 41 | 42 | CBox cursorRectangle = {0, 0, 0, 0}; 43 | 44 | bool operator==(const STextInputV1& other) { 45 | return other.client == client && other.resourceCaller == resourceCaller && other.resourceImpl == resourceImpl; 46 | } 47 | }; 48 | 49 | class CTextInputV1ProtocolManager { 50 | public: 51 | CTextInputV1ProtocolManager(); 52 | 53 | void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); 54 | void createTI(wl_client* client, wl_resource* resource, uint32_t id); 55 | void removeTI(STextInputV1* pTI); 56 | 57 | void displayDestroy(); 58 | 59 | // handlers for tiv1 60 | void handleActivate(wl_client* client, wl_resource* resource, wl_resource* seat, wl_resource* surface); 61 | void handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat); 62 | void handleShowInputPanel(wl_client* client, wl_resource* resource); 63 | void handleHideInputPanel(wl_client* client, wl_resource* resource); 64 | void handleReset(wl_client* client, wl_resource* resource); 65 | void handleSetSurroundingText(wl_client* client, wl_resource* resource, const char* text, uint32_t cursor, uint32_t anchor); 66 | void handleSetContentType(wl_client* client, wl_resource* resource, uint32_t hint, uint32_t purpose); 67 | void handleSetCursorRectangle(wl_client* client, wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height); 68 | void handleSetPreferredLanguage(wl_client* client, wl_resource* resource, const char* language); 69 | void handleCommitState(wl_client* client, wl_resource* resource, uint32_t serial); 70 | void handleInvokeAction(wl_client* client, wl_resource* resource, uint32_t button, uint32_t index); 71 | 72 | private: 73 | wl_global* m_pGlobal = nullptr; 74 | wl_listener m_liDisplayDestroy; 75 | 76 | std::vector> m_pClients; 77 | }; -------------------------------------------------------------------------------- /src/protocols/ToplevelExport.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" 5 | #include "hyprland-toplevel-export-v1-protocol.h" 6 | #include "Screencopy.hpp" 7 | 8 | #include 9 | #include 10 | 11 | class CMonitor; 12 | class CWindow; 13 | 14 | class CToplevelExportProtocolManager { 15 | public: 16 | CToplevelExportProtocolManager(); 17 | 18 | void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id); 19 | void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, CWindow* handle); 20 | void removeClient(CScreencopyClient* client, bool force = false); 21 | void removeFrame(SScreencopyFrame* frame, bool force = false); 22 | void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage); 23 | void displayDestroy(); 24 | void onWindowUnmap(CWindow* pWindow); 25 | void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e); 26 | 27 | private: 28 | wl_global* m_pGlobal = nullptr; 29 | std::list m_lFrames; 30 | std::list m_lClients; 31 | 32 | wl_listener m_liDisplayDestroy; 33 | 34 | std::vector m_vFramesAwaitingWrite; 35 | 36 | void shareFrame(SScreencopyFrame* frame); 37 | bool copyFrameDmabuf(SScreencopyFrame* frame, timespec* now); 38 | bool copyFrameShm(SScreencopyFrame* frame, timespec* now); 39 | void sendDamage(SScreencopyFrame* frame); 40 | 41 | friend class CScreencopyClient; 42 | }; -------------------------------------------------------------------------------- /src/protocols/WaylandProtocol.cpp: -------------------------------------------------------------------------------- 1 | #include "WaylandProtocol.hpp" 2 | #include "../Compositor.hpp" 3 | 4 | static void resourceDestroyNotify(wl_listener* listener, void* data) { 5 | CWaylandResource* pResource = wl_container_of(listener, pResource, m_liResourceDestroy); 6 | pResource->markDefunct(); 7 | } 8 | 9 | CWaylandResource::CWaylandResource(wl_client* client, const wl_interface* wlInterface, uint32_t version, uint32_t id) { 10 | m_pWLResource = wl_resource_create(client, wlInterface, version, id); 11 | 12 | if (!m_pWLResource) { 13 | wl_client_post_no_memory(client); 14 | return; 15 | } 16 | 17 | wl_resource_set_user_data(m_pWLResource, this); 18 | 19 | m_pWLClient = client; 20 | 21 | wl_list_init(&m_liResourceDestroy.link); 22 | m_liResourceDestroy.notify = resourceDestroyNotify; 23 | wl_resource_add_destroy_listener(m_pWLResource, &m_liResourceDestroy); 24 | 25 | Debug::log(TRACE, "[wl res {:x}] created", (uintptr_t)m_pWLResource); 26 | } 27 | 28 | void CWaylandResource::markDefunct() { 29 | if (m_bDefunct) 30 | return; 31 | 32 | Debug::log(TRACE, "[wl res {:x}] now defunct", (uintptr_t)m_pWLResource); 33 | m_bDefunct = true; 34 | wl_resource_set_user_data(m_pWLResource, nullptr); 35 | } 36 | 37 | CWaylandResource::~CWaylandResource() { 38 | const bool DESTROY = m_pWLResource && !m_bDefunct; 39 | 40 | wl_list_remove(&m_liResourceDestroy.link); 41 | wl_list_init(&m_liResourceDestroy.link); 42 | 43 | Debug::log(TRACE, "[wl res {:x}] destroying (wl_resource_destroy will be {})", (uintptr_t)m_pWLResource, (DESTROY ? "sent" : "not sent")); 44 | 45 | if (DESTROY) 46 | wl_resource_destroy(m_pWLResource); 47 | } 48 | 49 | bool CWaylandResource::good() { 50 | return m_pWLResource && !m_bDefunct; 51 | } 52 | 53 | wl_resource* CWaylandResource::resource() { 54 | return m_pWLResource; 55 | } 56 | 57 | uint32_t CWaylandResource::version() { 58 | RASSERT(good(), "Attempted to call version() on a bad resource"); 59 | 60 | return wl_resource_get_version(m_pWLResource); 61 | } 62 | 63 | void CWaylandResource::setImplementation(const void* impl, wl_resource_destroy_func_t df) { 64 | RASSERT(good(), "Attempted to call setImplementation() on a bad resource"); 65 | RASSERT(!m_bImplementationSet, "Wayland Resource {:x} already has an implementation, cannot re-set!", (uintptr_t)m_pWLResource); 66 | 67 | wl_resource_set_implementation(m_pWLResource, impl, this, df); 68 | 69 | Debug::log(TRACE, "[wl res {:x}] set impl to {:x}", (uintptr_t)m_pWLResource, (uintptr_t)impl); 70 | 71 | m_bImplementationSet = true; 72 | } 73 | 74 | void CWaylandResource::setData(void* data) { 75 | Debug::log(TRACE, "[wl res {:x}] set data to {:x}", (uintptr_t)m_pWLResource, (uintptr_t)data); 76 | m_pData = data; 77 | } 78 | 79 | void* CWaylandResource::data() { 80 | return m_pData; 81 | } 82 | 83 | static void bindManagerInternal(wl_client* client, void* data, uint32_t ver, uint32_t id) { 84 | ((IWaylandProtocol*)data)->bindManager(client, data, ver, id); 85 | } 86 | 87 | static void displayDestroyInternal(struct wl_listener* listener, void* data) { 88 | ((IWaylandProtocol*)data)->onDisplayDestroy(); 89 | } 90 | 91 | void IWaylandProtocol::onDisplayDestroy() { 92 | wl_global_destroy(m_pGlobal); 93 | } 94 | 95 | IWaylandProtocol::IWaylandProtocol(const wl_interface* iface, const int& ver, const std::string& name) { 96 | m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, iface, ver, this, &bindManagerInternal); 97 | 98 | if (!m_pGlobal) { 99 | Debug::log(ERR, "[proto {}] could not create a global", name); 100 | return; 101 | } 102 | 103 | m_liDisplayDestroy.notify = displayDestroyInternal; 104 | wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy); 105 | 106 | Debug::log(LOG, "[proto {}] started", name); 107 | } 108 | 109 | IWaylandProtocol::~IWaylandProtocol() { 110 | onDisplayDestroy(); 111 | } 112 | -------------------------------------------------------------------------------- /src/protocols/WaylandProtocol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | #define RESOURCE_OR_BAIL(resname) \ 6 | const auto resname = (CWaylandResource*)wl_resource_get_user_data(resource); \ 7 | if (!resname) \ 8 | return; 9 | 10 | class CWaylandResource { 11 | public: 12 | CWaylandResource(wl_client* client, const wl_interface* wlInterface, uint32_t version, uint32_t id); 13 | ~CWaylandResource(); 14 | 15 | bool good(); 16 | wl_resource* resource(); 17 | uint32_t version(); 18 | 19 | void setImplementation(const void* impl, wl_resource_destroy_func_t df); 20 | 21 | wl_listener m_liResourceDestroy; // private but has to be public 22 | void markDefunct(); 23 | 24 | void* data(); 25 | void setData(void* data); 26 | 27 | private: 28 | bool m_bImplementationSet = false; 29 | bool m_bDefunct = false; // m_liResourceDestroy fired 30 | wl_client* m_pWLClient = nullptr; 31 | wl_resource* m_pWLResource = nullptr; 32 | void* m_pData = nullptr; 33 | }; 34 | 35 | class IWaylandProtocol { 36 | public: 37 | IWaylandProtocol(const wl_interface* iface, const int& ver, const std::string& name); 38 | ~IWaylandProtocol(); 39 | 40 | virtual void onDisplayDestroy(); 41 | 42 | virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) = 0; 43 | 44 | private: 45 | wl_global* m_pGlobal = nullptr; 46 | wl_listener m_liDisplayDestroy; 47 | }; -------------------------------------------------------------------------------- /src/protocols/XDGOutput.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "WaylandProtocol.hpp" 4 | #include 5 | 6 | class CMonitor; 7 | 8 | struct SXDGOutput { 9 | CMonitor* monitor = nullptr; 10 | std::unique_ptr resource; 11 | 12 | std::optional overridePosition; 13 | 14 | wl_client* client = nullptr; 15 | bool isXWayland = false; 16 | }; 17 | 18 | class CXDGOutputProtocol : public IWaylandProtocol { 19 | public: 20 | CXDGOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name); 21 | 22 | virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); 23 | 24 | void onManagerResourceDestroy(wl_resource* res); 25 | void onOutputResourceDestroy(wl_resource* res); 26 | void onManagerGetXDGOutput(wl_client* client, wl_resource* resource, uint32_t id, wl_resource* outputResource); 27 | 28 | private: 29 | void updateOutputDetails(SXDGOutput* pOutput); 30 | void updateAllOutputs(); 31 | 32 | std::vector> m_vManagerResources; 33 | std::vector> m_vXDGOutputs; 34 | }; -------------------------------------------------------------------------------- /src/render/Framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Framebuffer.hpp" 2 | #include "OpenGL.hpp" 3 | 4 | bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { 5 | bool firstAlloc = false; 6 | RASSERT((w > 1 && h > 1), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); 7 | 8 | uint32_t glFormat = drmFormatToGL(drmFormat); 9 | uint32_t glType = glFormat != GL_RGBA ? 10 | #ifdef GLES2 11 | GL_UNSIGNED_INT_2_10_10_10_REV_EXT : 12 | #else 13 | GL_UNSIGNED_INT_2_10_10_10_REV : 14 | #endif 15 | GL_UNSIGNED_BYTE; 16 | 17 | if (m_iFb == (uint32_t)-1) { 18 | firstAlloc = true; 19 | glGenFramebuffers(1, &m_iFb); 20 | } 21 | 22 | if (m_cTex.m_iTexID == 0) { 23 | firstAlloc = true; 24 | glGenTextures(1, &m_cTex.m_iTexID); 25 | glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); 26 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 27 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 28 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 29 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 30 | } 31 | 32 | if (firstAlloc || m_vSize != Vector2D(w, h)) { 33 | glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); 34 | glTexImage2D(GL_TEXTURE_2D, 0, glFormat, w, h, 0, GL_RGBA, glType, 0); 35 | 36 | glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); 37 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex.m_iTexID, 0); 38 | 39 | // TODO: Allow this with gles2 40 | #ifndef GLES2 41 | if (m_pStencilTex) { 42 | glBindTexture(GL_TEXTURE_2D, m_pStencilTex->m_iTexID); 43 | glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0); 44 | 45 | glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); 46 | 47 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0); 48 | } 49 | #endif 50 | 51 | auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 52 | RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {})", status); 53 | 54 | Debug::log(LOG, "Framebuffer created, status {}", status); 55 | } 56 | 57 | glBindTexture(GL_TEXTURE_2D, 0); 58 | glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iCurrentOutputFb); 59 | 60 | m_vSize = Vector2D(w, h); 61 | 62 | return true; 63 | } 64 | 65 | void CFramebuffer::bind() { 66 | #ifndef GLES2 67 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb); 68 | #else 69 | glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); 70 | #endif 71 | glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y); 72 | } 73 | 74 | void CFramebuffer::release() { 75 | if (m_iFb != (uint32_t)-1 && m_iFb) 76 | glDeleteFramebuffers(1, &m_iFb); 77 | 78 | if (m_cTex.m_iTexID) 79 | glDeleteTextures(1, &m_cTex.m_iTexID); 80 | 81 | m_cTex.m_iTexID = 0; 82 | m_iFb = -1; 83 | m_vSize = Vector2D(); 84 | } 85 | 86 | CFramebuffer::~CFramebuffer() { 87 | release(); 88 | } 89 | 90 | bool CFramebuffer::isAllocated() { 91 | return m_iFb != (GLuint)-1; 92 | } -------------------------------------------------------------------------------- /src/render/Framebuffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include "Texture.hpp" 5 | 6 | class CFramebuffer { 7 | public: 8 | ~CFramebuffer(); 9 | 10 | bool alloc(int w, int h, uint32_t format = GL_RGBA); 11 | void bind(); 12 | void release(); 13 | void reset(); 14 | bool isAllocated(); 15 | 16 | Vector2D m_vSize; 17 | 18 | CTexture m_cTex; 19 | GLuint m_iFb = -1; 20 | 21 | CTexture* m_pStencilTex = nullptr; 22 | }; -------------------------------------------------------------------------------- /src/render/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.hpp" 2 | 3 | GLint CShader::getUniformLocation(const std::string& unif) { 4 | const auto itpos = m_muUniforms.find(unif); 5 | 6 | if (itpos == m_muUniforms.end()) { 7 | const auto unifLoc = glGetUniformLocation(program, unif.c_str()); 8 | m_muUniforms[unif] = unifLoc; 9 | return unifLoc; 10 | } 11 | 12 | return itpos->second; 13 | } 14 | 15 | CShader::~CShader() { 16 | destroy(); 17 | } 18 | 19 | void CShader::destroy() { 20 | glDeleteProgram(program); 21 | 22 | program = 0; 23 | } -------------------------------------------------------------------------------- /src/render/Shader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | #include 5 | 6 | class CShader { 7 | public: 8 | ~CShader(); 9 | 10 | GLuint program = 0; 11 | GLint proj = -1; 12 | GLint color = -1; 13 | GLint alphaMatte = -1; 14 | GLint tex = -1; 15 | GLint alpha = -1; 16 | GLint posAttrib = -1; 17 | GLint texAttrib = -1; 18 | GLint matteTexAttrib = -1; 19 | GLint discardOpaque = -1; 20 | GLint discardAlpha = -1; 21 | GLfloat discardAlphaValue = -1; 22 | 23 | GLint topLeft = -1; 24 | GLint bottomRight = -1; 25 | GLint fullSize = -1; 26 | GLint fullSizeUntransformed = -1; 27 | GLint radius = -1; 28 | GLint radiusOuter = -1; 29 | 30 | GLint thick = -1; 31 | 32 | GLint halfpixel = -1; 33 | 34 | GLint range = -1; 35 | GLint shadowPower = -1; 36 | GLint useAlphaMatte = -1; // always inverted 37 | 38 | GLint applyTint = -1; 39 | GLint tint = -1; 40 | 41 | GLint gradient = -1; 42 | GLint gradientLength = -1; 43 | GLint angle = -1; 44 | 45 | GLint time = -1; 46 | GLint distort = -1; 47 | GLint output = -1; 48 | 49 | GLint noise = -1; 50 | GLint contrast = -1; 51 | GLint brightness = -1; 52 | 53 | GLint getUniformLocation(const std::string&); 54 | 55 | void destroy(); 56 | 57 | private: 58 | std::unordered_map m_muUniforms; 59 | }; -------------------------------------------------------------------------------- /src/render/Shaders.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shaders/Textures.hpp" 4 | #include "shaders/Shadow.hpp" 5 | #include "shaders/Border.hpp" -------------------------------------------------------------------------------- /src/render/Texture.cpp: -------------------------------------------------------------------------------- 1 | #include "Texture.hpp" 2 | 3 | CTexture::CTexture() { 4 | // naffin' 5 | } 6 | 7 | CTexture::CTexture(wlr_texture* tex) { 8 | RASSERT(wlr_texture_is_gles2(tex), "wlr_texture provided to CTexture that isn't GLES2!"); 9 | wlr_gles2_texture_attribs attrs; 10 | wlr_gles2_texture_get_attribs(tex, &attrs); 11 | 12 | m_iTarget = attrs.target; 13 | m_iTexID = attrs.tex; 14 | 15 | if (m_iTarget == GL_TEXTURE_2D) 16 | m_iType = attrs.has_alpha ? TEXTURE_RGBA : TEXTURE_RGBX; 17 | else 18 | m_iType = TEXTURE_EXTERNAL; 19 | 20 | m_vSize = Vector2D(tex->width, tex->height); 21 | } 22 | 23 | void CTexture::destroyTexture() { 24 | if (m_iTexID) { 25 | glDeleteTextures(1, &m_iTexID); 26 | m_iTexID = 0; 27 | } 28 | } 29 | 30 | void CTexture::allocate() { 31 | if (!m_iTexID) 32 | glGenTextures(1, &m_iTexID); 33 | } -------------------------------------------------------------------------------- /src/render/Texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../defines.hpp" 4 | 5 | enum TEXTURETYPE 6 | { 7 | TEXTURE_INVALID, // Invalid 8 | TEXTURE_RGBA, // 4 channels 9 | TEXTURE_RGBX, // discard A 10 | TEXTURE_EXTERNAL, // EGLImage 11 | }; 12 | 13 | class CTexture { 14 | public: 15 | CTexture(); 16 | CTexture(wlr_texture*); 17 | 18 | void destroyTexture(); 19 | void allocate(); 20 | 21 | TEXTURETYPE m_iType = TEXTURE_RGBA; 22 | GLenum m_iTarget = GL_TEXTURE_2D; 23 | GLuint m_iTexID = 0; 24 | Vector2D m_vSize; 25 | }; -------------------------------------------------------------------------------- /src/render/Transformer.cpp: -------------------------------------------------------------------------------- 1 | #include "Transformer.hpp" 2 | 3 | void IWindowTransformer::preWindowRender(SRenderData* pRenderData) { 4 | ; 5 | } -------------------------------------------------------------------------------- /src/render/Transformer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Framebuffer.hpp" 4 | 5 | struct SRenderData; 6 | 7 | // A window transformer can be attached to a window. 8 | // If any is attached, Hyprland will render the window to a separate fb, then call the transform() func with it, 9 | // and finally render it back to the main fb after all transformers pass. 10 | // 11 | // Worth noting transformers for now only affect the main pass (not popups) 12 | class IWindowTransformer { 13 | public: 14 | // called by Hyprland. For more data about what is being rendered, inspect render data. 15 | // returns the out fb. 16 | virtual CFramebuffer* transform(CFramebuffer* in) = 0; 17 | 18 | // called by Hyprland before a window main pass is started. 19 | virtual void preWindowRender(SRenderData* pRenderData); 20 | }; -------------------------------------------------------------------------------- /src/render/decorations/CHyprDropShadowDecoration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IHyprWindowDecoration.hpp" 4 | 5 | class CHyprDropShadowDecoration : public IHyprWindowDecoration { 6 | public: 7 | CHyprDropShadowDecoration(CWindow*); 8 | virtual ~CHyprDropShadowDecoration(); 9 | 10 | virtual SWindowDecorationExtents getWindowDecorationExtents(); 11 | 12 | virtual void draw(CMonitor*, float a, const Vector2D& offset); 13 | 14 | virtual eDecorationType getDecorationType(); 15 | 16 | virtual void updateWindow(CWindow*); 17 | 18 | virtual void damageEntire(); 19 | 20 | virtual eDecorationLayer getDecorationLayer(); 21 | 22 | private: 23 | SWindowDecorationExtents m_seExtents; 24 | 25 | CWindow* m_pWindow = nullptr; 26 | 27 | Vector2D m_vLastWindowPos; 28 | Vector2D m_vLastWindowSize; 29 | 30 | CBox m_bLastWindowBox = {0}; 31 | SWindowDecorationExtents m_eLastExtents = {}; 32 | }; -------------------------------------------------------------------------------- /src/render/decorations/CHyprGroupBarDecoration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IHyprWindowDecoration.hpp" 4 | #include 5 | #include "../Texture.hpp" 6 | #include 7 | #include 8 | 9 | class CTitleTex { 10 | public: 11 | CTitleTex(CWindow* pWindow, const Vector2D& bufferSize); 12 | ~CTitleTex(); 13 | 14 | CTexture tex; 15 | std::string szContent; 16 | CWindow* pWindowOwner = nullptr; 17 | }; 18 | 19 | class CHyprGroupBarDecoration : public IHyprWindowDecoration { 20 | public: 21 | CHyprGroupBarDecoration(CWindow*); 22 | virtual ~CHyprGroupBarDecoration(); 23 | 24 | virtual SWindowDecorationExtents getWindowDecorationExtents(); 25 | 26 | virtual void draw(CMonitor*, float a, const Vector2D& offset); 27 | 28 | virtual eDecorationType getDecorationType(); 29 | 30 | virtual void updateWindow(CWindow*); 31 | 32 | virtual void damageEntire(); 33 | 34 | virtual SWindowDecorationExtents getWindowDecorationReservedArea(); 35 | 36 | virtual void onBeginWindowDragOnDeco(const Vector2D&); 37 | 38 | virtual bool onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&); 39 | 40 | virtual void onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*); 41 | 42 | virtual eDecorationLayer getDecorationLayer(); 43 | 44 | virtual uint64_t getDecorationFlags(); 45 | 46 | private: 47 | SWindowDecorationExtents m_seExtents; 48 | 49 | CWindow* m_pWindow = nullptr; 50 | 51 | Vector2D m_vLastWindowPos; 52 | Vector2D m_vLastWindowSize; 53 | 54 | std::deque m_dwGroupMembers; 55 | 56 | float m_fBarWidth; 57 | 58 | CTitleTex* textureFromTitle(const std::string&); 59 | void invalidateTextures(); 60 | 61 | void refreshGradients(); 62 | 63 | struct STitleTexs { 64 | // STitleTexs* overriden = nullptr; // TODO: make shit shared in-group to decrease VRAM usage. 65 | std::deque> titleTexs; 66 | } m_sTitleTexs; 67 | }; 68 | -------------------------------------------------------------------------------- /src/render/decorations/IHyprWindowDecoration.cpp: -------------------------------------------------------------------------------- 1 | #include "IHyprWindowDecoration.hpp" 2 | 3 | #include "../../Window.hpp" 4 | 5 | IHyprWindowDecoration::IHyprWindowDecoration(CWindow* pWindow) { 6 | m_pWindow = pWindow; 7 | } 8 | 9 | IHyprWindowDecoration::~IHyprWindowDecoration() {} 10 | 11 | SWindowDecorationExtents IHyprWindowDecoration::getWindowDecorationReservedArea() { 12 | return SWindowDecorationExtents{}; 13 | } 14 | 15 | CRegion IHyprWindowDecoration::getWindowDecorationRegion() { 16 | const SWindowDecorationExtents RESERVED = getWindowDecorationReservedArea(); 17 | const int BORDERSIZE = m_pWindow->getRealBorderSize(); 18 | return CRegion(m_pWindow->m_vRealPosition.vec().x - (BORDERSIZE + RESERVED.topLeft.x) * (int)(RESERVED.topLeft.x != 0), 19 | m_pWindow->m_vRealPosition.vec().y - (BORDERSIZE + RESERVED.topLeft.y) * (int)(RESERVED.topLeft.y != 0), 20 | m_pWindow->m_vRealSize.vec().x + (BORDERSIZE + RESERVED.topLeft.x) * (int)(RESERVED.topLeft.x != 0) + 21 | (BORDERSIZE + RESERVED.bottomRight.x) * (int)(RESERVED.bottomRight.x != 0), 22 | m_pWindow->m_vRealSize.vec().y + (BORDERSIZE + RESERVED.topLeft.y) * (int)(RESERVED.topLeft.y != 0) + 23 | (BORDERSIZE + RESERVED.bottomRight.y) * (int)(RESERVED.bottomRight.y != 0)) 24 | .subtract(CRegion(m_pWindow->m_vRealPosition.vec().x - BORDERSIZE, m_pWindow->m_vRealPosition.vec().y - BORDERSIZE, m_pWindow->m_vRealSize.vec().x + 2 * BORDERSIZE, 25 | m_pWindow->m_vRealSize.vec().y + 2 * BORDERSIZE)); 26 | } 27 | 28 | void IHyprWindowDecoration::onBeginWindowDragOnDeco(const Vector2D&) { 29 | ; 30 | } 31 | 32 | bool IHyprWindowDecoration::onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&) { 33 | return true; 34 | } 35 | 36 | void IHyprWindowDecoration::onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*) { 37 | ; 38 | } 39 | 40 | eDecorationLayer IHyprWindowDecoration::getDecorationLayer() { 41 | return DECORATION_LAYER_UNDER; 42 | } 43 | 44 | uint64_t IHyprWindowDecoration::getDecorationFlags() { 45 | return 0; 46 | } -------------------------------------------------------------------------------- /src/render/decorations/IHyprWindowDecoration.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../defines.hpp" 4 | #include "../../helpers/Region.hpp" 5 | 6 | enum eDecorationType 7 | { 8 | DECORATION_NONE = -1, 9 | DECORATION_GROUPBAR, 10 | DECORATION_SHADOW, 11 | DECORATION_CUSTOM 12 | }; 13 | 14 | enum eDecorationLayer 15 | { 16 | DECORATION_LAYER_BOTTOM = 0, /* lowest. */ 17 | DECORATION_LAYER_UNDER, /* under the window, but above BOTTOM */ 18 | DECORATION_LAYER_OVER, /* above the window, but below its popups */ 19 | DECORATION_LAYER_OVERLAY /* above everything of the window, including popups */ 20 | }; 21 | 22 | enum eDecorationFlags 23 | { 24 | DECORATION_ALLOWS_MOUSE_INPUT = 1 << 0, /* this decoration accepts mouse input */ 25 | DECORATION_PART_OF_MAIN_WINDOW = 1 << 1, /* this decoration is a *seamless* part of the main window, so stuff like shadows will include it */ 26 | }; 27 | 28 | class CWindow; 29 | class CMonitor; 30 | 31 | class IHyprWindowDecoration { 32 | public: 33 | IHyprWindowDecoration(CWindow*); 34 | virtual ~IHyprWindowDecoration() = 0; 35 | 36 | virtual SWindowDecorationExtents getWindowDecorationExtents() = 0; 37 | 38 | virtual void draw(CMonitor*, float a, const Vector2D& offset = Vector2D()) = 0; 39 | 40 | virtual eDecorationType getDecorationType() = 0; 41 | 42 | virtual void updateWindow(CWindow*) = 0; 43 | 44 | virtual void damageEntire() = 0; 45 | 46 | virtual SWindowDecorationExtents getWindowDecorationReservedArea(); 47 | 48 | virtual CRegion getWindowDecorationRegion(); 49 | 50 | virtual void onBeginWindowDragOnDeco(const Vector2D&); // called when the user calls the "movewindow" mouse dispatcher on the deco 51 | 52 | virtual bool onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&); // returns true if the window should be placed by the layout 53 | 54 | virtual void onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*); 55 | 56 | virtual eDecorationLayer getDecorationLayer(); 57 | 58 | virtual uint64_t getDecorationFlags(); 59 | 60 | private: 61 | CWindow* m_pWindow = nullptr; 62 | }; 63 | -------------------------------------------------------------------------------- /src/render/shaders/Border.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // makes a stencil without corners 6 | inline const std::string FRAGBORDER1 = R"#( 7 | precision mediump float; 8 | varying vec4 v_color; 9 | varying vec2 v_texcoord; 10 | 11 | uniform vec2 topLeft; 12 | uniform vec2 fullSize; 13 | uniform vec2 fullSizeUntransformed; 14 | uniform float radius; 15 | uniform float radiusOuter; 16 | uniform float thick; 17 | 18 | uniform vec4 gradient[10]; 19 | uniform int gradientLength; 20 | uniform float angle; 21 | uniform float alpha; 22 | 23 | vec4 getColorForCoord(vec2 normalizedCoord) { 24 | if (gradientLength < 2) 25 | return gradient[0]; 26 | 27 | float finalAng = 0.0; 28 | 29 | if (angle > 4.71 /* 270 deg */) { 30 | normalizedCoord[1] = 1.0 - normalizedCoord[1]; 31 | finalAng = 6.28 - angle; 32 | } else if (angle > 3.14 /* 180 deg */) { 33 | normalizedCoord[0] = 1.0 - normalizedCoord[0]; 34 | normalizedCoord[1] = 1.0 - normalizedCoord[1]; 35 | finalAng = angle - 3.14; 36 | } else if (angle > 1.57 /* 90 deg */) { 37 | normalizedCoord[0] = 1.0 - normalizedCoord[0]; 38 | finalAng = 3.14 - angle; 39 | } else { 40 | finalAng = angle; 41 | } 42 | 43 | float sine = sin(finalAng); 44 | 45 | float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); 46 | int bottom = int(floor(progress)); 47 | int top = bottom + 1; 48 | 49 | return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); 50 | } 51 | 52 | void main() { 53 | 54 | highp vec2 pixCoord = vec2(gl_FragCoord); 55 | highp vec2 pixCoordOuter = pixCoord; 56 | highp vec2 originalPixCoord = v_texcoord; 57 | originalPixCoord *= fullSizeUntransformed; 58 | float additionalAlpha = 1.0; 59 | 60 | vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); 61 | 62 | bool done = false; 63 | 64 | pixCoord -= topLeft + fullSize * 0.5; 65 | pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; 66 | pixCoordOuter = pixCoord; 67 | pixCoord -= fullSize * 0.5 - radius; 68 | pixCoordOuter -= fullSize * 0.5 - radiusOuter; 69 | 70 | // center the pixes dont make it top-left 71 | pixCoord += vec2(1.0, 1.0) / fullSize; 72 | pixCoordOuter += vec2(1.0, 1.0) / fullSize; 73 | 74 | if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { 75 | float dist = length(pixCoord); 76 | float distOuter = length(pixCoordOuter); 77 | float h = (thick / 2.0); 78 | 79 | if (dist < radius - h) { 80 | // lower 81 | float normalized = smoothstep(0.0, 1.0, dist - radius + thick + 0.5); 82 | additionalAlpha *= normalized; 83 | done = true; 84 | } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { 85 | // higher 86 | float normalized = 1.0 - smoothstep(0.0, 1.0, distOuter - radiusOuter + 0.5); 87 | additionalAlpha *= normalized; 88 | done = true; 89 | } else if (distOuter < radiusOuter - h) { 90 | additionalAlpha = 1.0; 91 | done = true; 92 | } 93 | } 94 | 95 | // now check for other shit 96 | if (!done) { 97 | // distance to all straight bb borders 98 | float distanceT = originalPixCoord[1]; 99 | float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; 100 | float distanceL = originalPixCoord[0]; 101 | float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; 102 | 103 | // get the smallest 104 | float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); 105 | 106 | if (smallest > thick) 107 | discard; 108 | } 109 | 110 | if (additionalAlpha == 0.0) 111 | discard; 112 | 113 | pixColor = getColorForCoord(v_texcoord); 114 | pixColor.rgb *= pixColor[3]; 115 | 116 | pixColor *= alpha * additionalAlpha; 117 | 118 | gl_FragColor = pixColor; 119 | } 120 | )#"; 121 | -------------------------------------------------------------------------------- /src/render/shaders/Shadow.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | inline const std::string FRAGSHADOW = R"#( 6 | precision mediump float; 7 | varying vec4 v_color; 8 | uniform sampler2D alphaMatte; 9 | varying vec2 v_texcoord; 10 | varying vec2 v_texcoordMatte; 11 | 12 | uniform vec2 topLeft; 13 | uniform vec2 bottomRight; 14 | uniform vec2 fullSize; 15 | uniform float radius; 16 | uniform float range; 17 | uniform float shadowPower; 18 | uniform int useAlphaMatte; 19 | 20 | float pixAlphaRoundedDistance(float distanceToCorner) { 21 | if (distanceToCorner > radius) { 22 | return 0.0; 23 | } 24 | 25 | if (distanceToCorner > radius - range) { 26 | return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? 27 | } 28 | 29 | return 1.0; 30 | } 31 | 32 | void main() { 33 | 34 | vec4 pixColor = v_color; 35 | float originalAlpha = pixColor[3]; 36 | 37 | bool done = false; 38 | 39 | vec2 pixCoord = fullSize * v_texcoord; 40 | 41 | // ok, now we check the distance to a border. 42 | 43 | if (pixCoord[0] < topLeft[0]) { 44 | if (pixCoord[1] < topLeft[1]) { 45 | // top left 46 | pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(distance(pixCoord, topLeft)); 47 | done = true; 48 | } else if (pixCoord[1] > bottomRight[1]) { 49 | // bottom left 50 | pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(distance(pixCoord, vec2(topLeft[0], bottomRight[1]))); 51 | done = true; 52 | } 53 | } else if (pixCoord[0] > bottomRight[0]) { 54 | if (pixCoord[1] < topLeft[1]) { 55 | // top right 56 | pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(distance(pixCoord, vec2(bottomRight[0], topLeft[1]))); 57 | done = true; 58 | } else if (pixCoord[1] > bottomRight[1]) { 59 | // bottom right 60 | pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(distance(pixCoord, bottomRight)); 61 | done = true; 62 | } 63 | } 64 | 65 | if (!done) { 66 | // distance to all straight bb borders 67 | float distanceT = pixCoord[1]; 68 | float distanceB = fullSize[1] - pixCoord[1]; 69 | float distanceL = pixCoord[0]; 70 | float distanceR = fullSize[0] - pixCoord[0]; 71 | 72 | // get the smallest 73 | float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); 74 | 75 | if (smallest < range) { 76 | pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); 77 | } 78 | } 79 | 80 | if (useAlphaMatte == 1) { 81 | pixColor[3] *= 1.0 - texture2D(alphaMatte, v_texcoordMatte)[3]; 82 | } 83 | 84 | if (pixColor[3] == 0.0) { 85 | discard; return; 86 | } 87 | 88 | // premultiply 89 | pixColor.rgb *= pixColor[3]; 90 | 91 | gl_FragColor = pixColor; 92 | })#"; 93 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define GIT_COMMIT_HASH "@HASH@" 3 | #define GIT_BRANCH "@BRANCH@" 4 | #define GIT_COMMIT_MESSAGE "@MESSAGE@" 5 | #define GIT_DIRTY "@DIRTY@" 6 | #define GIT_TAG "@TAG@" -------------------------------------------------------------------------------- /subprojects/packagefiles/wlroots-meson-build.patch: -------------------------------------------------------------------------------- 1 | diff --git a/include/meson.build b/include/meson.build 2 | index e669800..687786b 100644 3 | --- a/include/meson.build 4 | +++ b/include/meson.build 5 | @@ -1,4 +1,5 @@ 6 | -subdir('wlr') 7 | +run_command('ln', '-sf', join_paths(meson.project_source_root(), 'include', 'wlr'), join_paths(meson.project_source_root(), 'include', 'wlroots'), check: true) 8 | +subdir('wlroots') 9 | 10 | exclude_files = ['meson.build', 'config.h.in', 'version.h.in'] 11 | if not features.get('drm-backend') 12 | @@ -24,8 +25,8 @@ if not features.get('session') 13 | exclude_files += 'backend/session.h' 14 | endif 15 | 16 | -install_subdir('wlr', 17 | - install_dir: get_option('includedir'), 18 | +install_subdir('wlroots', 19 | + install_dir: join_paths(get_option('includedir'), 'hyprland'), 20 | exclude_files: exclude_files, 21 | ) 22 | 23 | diff --git a/include/wlr/meson.build b/include/wlr/meson.build 24 | index f7ca413..0a86d54 100644 25 | --- a/include/wlr/meson.build 26 | +++ b/include/wlr/meson.build 27 | @@ -22,4 +22,4 @@ ver_h = configure_file( 28 | configuration: version_data, 29 | ) 30 | 31 | -install_headers(conf_h, ver_h, subdir: 'wlr') 32 | +install_headers(conf_h, ver_h, subdir: join_paths('hyprland', 'wlroots')) 33 | diff --git a/meson.build b/meson.build 34 | index 29b103a..0b6e5a4 100644 35 | --- a/meson.build 36 | +++ b/meson.build 37 | @@ -15,7 +15,7 @@ project( 38 | # necessary for bugfix releases. Increasing soversion is required because 39 | # wlroots never guarantees ABI stability -- only API stability is guaranteed 40 | # between minor releases. 41 | -soversion = 12 42 | +soversion = 12032 43 | 44 | little_endian = target_machine.endian() == 'little' 45 | big_endian = target_machine.endian() == 'big' 46 | -------------------------------------------------------------------------------- /subprojects/wlroots.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory = wlroots 3 | url = https://gitlab.freedesktop.org/wlroots/wlroots.git 4 | revision = 47bf87ade2bd32395615a385ebde1fefbcdf79a2 5 | depth = 1 6 | 7 | diff_files = wlroots-meson-build.patch 8 | --------------------------------------------------------------------------------