diff --git a/.clang-tidy b/.clang-tidy
index db499035..f0dfdf1b 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,111 +1,4 @@
-WarningsAsErrors: >
- -*,
- bugprone-*,
- -bugprone-multi-level-implicit-pointer-conversion,
- -bugprone-empty-catch,
- -bugprone-unused-return-value,
- -bugprone-reserved-identifier,
- -bugprone-switch-missing-default-case,
- -bugprone-unused-local-non-trivial-variable,
- -bugprone-easily-swappable-parameters,
- -bugprone-forward-declararion-namespace,
- -bugprone-forward-declararion-namespace,
- -bugprone-macro-parentheses,
- -bugprone-narrowing-conversions,
- -bugprone-branch-clone,
- -bugprone-assignment-in-if-condition,
- concurrency-*,
- -concurrency-mt-unsafe,
- cppcoreguidelines-*,
- -cppcoreguidelines-pro-type-const-cast,
- -cppcoreguidelines-owning-memory,
- -cppcoreguidelines-avoid-magic-numbers,
- -cppcoreguidelines-pro-bounds-constant-array-index,
- -cppcoreguidelines-avoid-const-or-ref-data-members,
- -cppcoreguidelines-non-private-member-variables-in-classes,
- -cppcoreguidelines-avoid-goto,
- -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
- -cppcoreguidelines-avoid-do-while,
- -cppcoreguidelines-avoid-non-const-global-variables,
- -cppcoreguidelines-special-member-functions,
- -cppcoreguidelines-explicit-virtual-functions,
- -cppcoreguidelines-avoid-c-arrays,
- -cppcoreguidelines-pro-bounds-pointer-arithmetic,
- -cppcoreguidelines-narrowing-conversions,
- -cppcoreguidelines-pro-type-union-access,
- -cppcoreguidelines-pro-type-member-init,
- -cppcoreguidelines-macro-usage,
- -cppcoreguidelines-macro-to-enum,
- -cppcoreguidelines-init-variables,
- -cppcoreguidelines-pro-type-cstyle-cast,
- -cppcoreguidelines-pro-type-vararg,
- -cppcoreguidelines-pro-type-reinterpret-cast,
- -google-global-names-in-headers,
- -google-readability-casting,
- google-runtime-operator,
- misc-*,
- -misc-use-internal-linkage,
- -misc-unused-parameters,
- -misc-no-recursion,
- -misc-non-private-member-variables-in-classes,
- -misc-include-cleaner,
- -misc-use-anonymous-namespace,
- -misc-const-correctness,
- modernize-*,
- -modernize-use-emplace,
- -modernize-redundant-void-arg,
- -modernize-use-starts-ends-with,
- -modernize-use-designated-initializers,
- -modernize-use-std-numbers,
- -modernize-return-braced-init-list,
- -modernize-use-trailing-return-type,
- -modernize-use-using,
- -modernize-use-override,
- -modernize-avoid-c-arrays,
- -modernize-macro-to-enum,
- -modernize-loop-convert,
- -modernize-use-nodiscard,
- -modernize-pass-by-value,
- -modernize-use-auto,
- performance-*,
- -performance-inefficient-vector-operation,
- -performance-inefficient-string-concatenation,
- -performance-enum-size,
- -performance-move-const-arg,
- -performance-avoid-endl,
- -performance-unnecessary-value-param,
- portability-std-allocator-const,
- readability-*,
- -readability-identifier-naming,
- -readability-use-std-min-max,
- -readability-math-missing-parentheses,
- -readability-simplify-boolean-expr,
- -readability-static-accessed-through-instance,
- -readability-use-anyofallof,
- -readability-enum-initial-value,
- -readability-redundant-inline-specifier,
- -readability-function-cognitive-complexity,
- -readability-function-size,
- -readability-identifier-length,
- -readability-magic-numbers,
- -readability-uppercase-literal-suffix,
- -readability-braces-around-statements,
- -readability-redundant-access-specifiers,
- -readability-else-after-return,
- -readability-container-data-pointer,
- -readability-implicit-bool-conversion,
- -readability-avoid-nested-conditional-operator,
- -readability-redundant-member-init,
- -readability-redundant-string-init,
- -readability-avoid-const-params-in-decls,
- -readability-named-parameter,
- -readability-convert-member-functions-to-static,
- -readability-qualified-auto,
- -readability-make-member-function-const,
- -readability-isolate-declaration,
- -readability-inconsistent-declaration-parameter-name,
- -clang-diagnostic-error,
-
+WarningsAsErrors: '*'
HeaderFilterRegex: '.*\.hpp'
FormatStyle: file
Checks: >
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index fd5797d5..5a20b890 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -1,15 +1,117 @@
-name: Do not open issues, go to discussions please!
-description: Do not open an issue
+name: Bug Report
+description: Something is not working right
+labels: ["bug"]
body:
- type: checkboxes
attributes:
- label: Please close this issue.
- description: Users cannot open issues. I want my issue to be closed.
+ label: Already reported ? *
+ description: Before opening a new bug report, please take a moment to search through the current open issues. If the same bug is already reported, don't open new issue - instead go upvote/comment on an existing one.
options:
- - label: Yes, I want this issue to be closed.
+ - label: I have searched the existing open and closed issues.
required: true
- - type: textarea
- id: body
+ - type: dropdown
+ id: type
attributes:
- label: Issue body
+ label: Regression?
+ description: |
+ Regression means that something used to work but no longer does.
+ **BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue
+ multiple: true
+ options:
+ - "Definitely a regression - something broke after update (requires bisect)"
+ - "Probably not a regression / I don't remember it happening before"
+ - "Not a regression - it's bug regarding new feature"
+ - "Not a regression - it's an old bug"
+ - "I don't know, I started using Hyprland only recently"
+ validations:
+ required: true
+
+ - type: textarea
+ id: ver
+ attributes:
+ label: System Info and Hyprland Version
+ description: |
+ Paste the output of `hyprctl systeminfo` here. If you can't
+ launch Hyprland, paste the output of `Hyprland --systeminfo`.
+ value: "
+ System/Version info
+
+
+ ```
+
+
+
+ ```
+
+
+ "
+ validations:
+ required: true
+
+ - type: textarea
+ id: desc
+ attributes:
+ label: Description
+ description: "What went wrong?"
+ validations:
+ required: true
+
+ - type: textarea
+ id: repro
+ attributes:
+ label: How to reproduce
+ description: "How can someone else reproduce the issue?"
+ placeholder: |
+ 1. ...
+ 2. ...
+ 3. ...
+
+ validations:
+ required: true
+
+ - type: markdown
+ attributes:
+ value: |
+ ## Additional info section
+
+ In the section below you will be asked to upload some files.
+
+ When including text files (such as logs or config), please **always ATTACH** them, and not paste them directly.
+
+ This is important to avoid clutter, spam, and make the issues more readable.
+ Thanks for your understanding.
+
+ # The main reason to disallow pasting directly or in a dropdown, is to not clutter
+ # the issue with unnecessary keywords, making the github issue search useless.
+ - type: checkboxes
+ attributes:
+ label: Attach not paste
+ options:
+ - label: I understand that all text files must be *attached*, and not pasted directly. If not respected, this issue will likely get closed as spam
+ required: true
+
+ - type: markdown
+ attributes:
+ value: >-
+ Please be sure to upload the following files below if they are relevant to the issue:
+
+ - Logs can be found in $XDG_RUNTIME_DIR/hypr (sort by date to grab the latest)
+ - Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
+ - Hyprland config files - `hyprctl systeminfo -c > /tmp/hyprland_config_dump.txt` use this command to dump full configuration to a single file.
+
+ - type: checkboxes
+ attributes:
+ label: Checklist of files to include below
+ options:
+ - label: Hyprland config - `hyprctl systeminfo -c` (always include)
+ - label: Crash report (always include in case of crash)
+ - label: Video (always include in case of a visual bug)
+ - label: Logs (might contain useful info such as errors)
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Additional info & File uploads
+ description: |
+ Tip: You can attach files by clicking this area to highlight it and then dragging files in.
diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
new file mode 100644
index 00000000..b34786a0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -0,0 +1,19 @@
+name: Feature Request
+description: I'd like to request additional functionality
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Before opening a new issue, take a moment to search through the current open ones.
+
+ ---
+
+ - type: textarea
+ id: desc
+ attributes:
+ label: Description
+ description: "Describe your idea"
+ validations:
+ required: true
+
diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml
index 9fcaabfb..b4c9cf78 100644
--- a/.github/actions/setup_base/action.yml
+++ b/.github/actions/setup_base/action.yml
@@ -20,11 +20,9 @@ runs:
clang \
cmake \
git \
- glaze \
glm \
glslang \
go \
- gtest \
hyprlang \
hyprcursor \
jq \
@@ -46,7 +44,6 @@ runs:
libxkbfile \
lld \
meson \
- muparser \
ninja \
pango \
pixman \
@@ -67,6 +64,15 @@ runs:
librsvg \
re2
+ - name: Get glaze
+ shell: bash
+ run: |
+ git clone https://github.com/stephenberry/glaze.git
+ cd glaze
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
+ cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
+ cmake --install build
+
- name: Get hyprwayland-scanner-git
shell: bash
run: |
@@ -76,25 +82,16 @@ runs:
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cmake --install build
- - name: Get hyprwire-git
+ - name: Get hyprgraphics-git
shell: bash
run: |
- git clone https://github.com/hyprwm/hyprwire --recursive
- cd hyprwire
- cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
- cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
- cmake --install build
+ git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build
- name: Get hyprutils-git
shell: bash
run: |
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
- - name: Get hyprgraphics-git
- shell: bash
- run: |
- git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build
-
- name: Get aquamarine-git
shell: bash
run: |
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 6b89255a..a0685fcf 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -22,10 +22,6 @@ protocols:
- changed-files:
- any-glob-to-any-file: ["protocols/**", "src/protocols/**"]
-start:
- - changed-files:
- - any-glob-to-any-file: "start/**"
-
core:
- changed-files:
- any-glob-to-any-file: "src/**"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 75b4b7c5..fda97f57 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,8 +1,6 @@
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 2ec558ed..06c1e197 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -21,19 +21,22 @@ jobs:
- name: Build Hyprland
run: |
- CFLAGS=-Werror CXXFLAGS=-Werror make nopch
+ CFLAGS=-Werror CXXFLAGS=-Werror make all
- name: Compress and package artifacts
run: |
mkdir x86_64-pc-linux-gnu
mkdir hyprland
+ mkdir hyprland/example
+ mkdir hyprland/assets
cp ./LICENSE hyprland/
cp build/Hyprland hyprland/
cp build/hyprctl/hyprctl hyprland/
cp build/hyprpm/hyprpm hyprland/
+ cp build/Hyprland hyprland/
cp -r example/ hyprland/
cp -r assets/ hyprland/
- tar -cvJf Hyprland.tar.xz hyprland
+ tar -cvf Hyprland.tar.xz hyprland
- name: Release
uses: actions/upload-artifact@v4
@@ -41,43 +44,86 @@ jobs:
name: Build archive
path: Hyprland.tar.xz
- clang-format:
- permissions: read-all
+ meson:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
- name: "Code Style"
+ name: "Build Hyprland with Meson (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- - name: Checkout repository
+ - name: Checkout repository actions
uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/actions
- # - name: clang-format check
- # uses: jidicula/clang-format-action@v4.16.0
- # with:
- # exclude-regex: ^subprojects$
+ - name: Setup base
+ uses: ./.github/actions/setup_base
- - name: Install clang-format
- run: |
- pacman --noconfirm --noprogressbar -Syyu
- pacman --noconfirm --noprogressbar -Sy clang
+ - name: Configure
+ run: meson setup build -Ddefault_library=static
+
+ - name: Compile
+ run: ninja -C build
+
+ no-pch:
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
+ name: "Build Hyprland without precompiled headers (Arch)"
+ runs-on: ubuntu-latest
+ container:
+ image: archlinux
+ steps:
+ - name: Checkout repository actions
+ uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/actions
+
+ - name: Setup base
+ uses: ./.github/actions/setup_base
+ with:
+ INSTALL_XORG_PKGS: true
+
+ - name: Compile
+ run: make nopch
+
+ noxwayland:
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
+ name: "Build Hyprland in pure Wayland (Arch)"
+ runs-on: ubuntu-latest
+ container:
+ image: archlinux
+ steps:
+ - name: Checkout repository actions
+ uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/actions
+
+ - name: Setup base
+ uses: ./.github/actions/setup_base
+
+ - name: Configure
+ run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja
+
+ - name: Compile
+ run: make release
+
+ clang-format:
+ permissions: read-all
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
+ name: "Code Style (Arch)"
+ runs-on: ubuntu-latest
+ container:
+ image: archlinux
+ steps:
+ - name: Checkout repository actions
+ uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/actions
+
+ - name: Setup base
+ uses: ./.github/actions/setup_base
+
+ - name: Configure
+ run: meson setup build -Ddefault_library=static
- name: clang-format check
- run: .github/workflows/clang-format-check.sh "." "llvm" "^subprojects$" ""
-
- - name: Save PR head commit SHA
- if: failure() && github.event_name == 'pull_request'
- shell: bash
- run: |
- SHA="${{ github.event.pull_request.head.sha }}"
- echo "SHA=$SHA" >> $GITHUB_ENV
- - name: Save latest commit SHA if not PR
- if: failure() && github.event_name != 'pull_request'
- shell: bash
- run: echo "SHA=${{ github.sha }}" >> $GITHUB_ENV
-
- - name: Report failure in job summary
- if: failure()
- run: |
- DEEPLINK="${{ github.server_url }}/${{ github.repository }}/commit/${{ env.SHA }}"
- echo -e "Format check failed on commit [${GITHUB_SHA:0:8}]($DEEPLINK) with files:\n$(<$GITHUB_WORKSPACE/failing-files.txt)" >> $GITHUB_STEP_SUMMARY
+ run: ninja -C build clang-format-check
diff --git a/.github/workflows/clang-format-check.sh b/.github/workflows/clang-format-check.sh
deleted file mode 100755
index 41237aa7..00000000
--- a/.github/workflows/clang-format-check.sh
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env bash
-#
-# Adapted from https://github.com/jidicula/clang-format-action
-
-###############################################################################
-# check.sh #
-###############################################################################
-# USAGE: ./entrypoint.sh [] []
-#
-# Checks all C/C++/Protobuf/CUDA files (.h, .H, .hpp, .hh, .h++, .hxx and .c,
-# .C, .cpp, .cc, .c++, .cxx, .proto, .cu) in the provided GitHub repository path
-# (arg1) for conforming to clang-format. If no path is provided or provided path
-# is not a directory, all C/C++/Protobuf/CUDA files are checked. If any files
-# are incorrectly formatted, the script lists them and exits with 1.
-#
-# Define your own formatting rules in a .clang-format file at your repository
-# root. Otherwise, the provided style guide (arg2) is used as a fallback.
-
-# format_diff function
-# Accepts a filepath argument. The filepath passed to this function must point
-# to a C/C++/Protobuf/CUDA file.
-format_diff() {
- local filepath="$1"
-
- # Invoke clang-format with dry run and formatting error output
- local_format="$(clang-format \
- --dry-run \
- --Werror \
- --style=file \
- --fallback-style="$FALLBACK_STYLE" \
- "${filepath}")"
-
- local format_status="$?"
- if [[ ${format_status} -ne 0 ]]; then
- # Append Markdown-bulleted monospaced filepath of failing file to
- # summary file.
- echo "* \`$filepath\`" >>failing-files.txt
-
- echo "Failed on file: $filepath" >&2
- echo "$local_format" >&2
- exit_code=1 # flip the global exit code
- return "${format_status}"
- fi
- return 0
-}
-
-CHECK_PATH="$1"
-FALLBACK_STYLE="$2"
-EXCLUDE_REGEX="$3"
-INCLUDE_REGEX="$4"
-
-# Set the regex to an empty string regex if nothing was provided
-if [[ -z $EXCLUDE_REGEX ]]; then
- EXCLUDE_REGEX="^$"
-fi
-
-# Set the filetype regex if nothing was provided.
-# Find all C/C++/Protobuf/CUDA files:
-# h, H, hpp, hh, h++, hxx
-# c, C, cpp, cc, c++, cxx
-# ino, pde
-# proto
-# cu
-if [[ -z $INCLUDE_REGEX ]]; then
- INCLUDE_REGEX='^.*\.((((c|C)(c|pp|xx|\+\+)?$)|((h|H)h?(pp|xx|\+\+)?$))|(ino|pde|proto|cu))$'
-fi
-
-cd "$GITHUB_WORKSPACE" || exit 2
-
-if [[ ! -d $CHECK_PATH ]]; then
- echo "Not a directory in the workspace, fallback to all files." >&2
- CHECK_PATH="."
-fi
-
-# initialize exit code
-exit_code=0
-
-# All files improperly formatted will be printed to the output.
-src_files=$(find "$CHECK_PATH" -name .git -prune -o -regextype posix-egrep -regex "$INCLUDE_REGEX" -print)
-
-# check formatting in each source file
-IFS=$'\n' # Loop below should separate on new lines, not spaces.
-for file in $src_files; do
- # Only check formatting if the path doesn't match the regex
- if ! [[ ${file} =~ $EXCLUDE_REGEX ]]; then
- format_diff "${file}"
- fi
-done
-
-# global exit code is flipped to nonzero if any invocation of `format_diff` has
-# a formatting difference.
-exit "$exit_code"
diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml
index 505829e3..e935a605 100644
--- a/.github/workflows/clang-format.yml
+++ b/.github/workflows/clang-format.yml
@@ -4,23 +4,43 @@ jobs:
clang-format:
permissions: write-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
- name: "Code Style"
+ name: "Code Style (Arch)"
runs-on: ubuntu-latest
+ container:
+ image: archlinux
steps:
- - name: Checkout repository
+ - name: Checkout repository actions
uses: actions/checkout@v4
+ with:
+ sparse-checkout: .github/actions
+
+ - name: Setup base
+ uses: ./.github/actions/setup_base
+
+ - name: Configure
+ run: meson setup build -Ddefault_library=static
- name: clang-format check
- uses: jidicula/clang-format-action@v4.16.0
- with:
- exclude-regex: ^subprojects$
+ run: ninja -C build clang-format-check
- - name: Create comment
+ - name: clang-format apply
+ if: ${{ failure() && github.event_name == 'pull_request' }}
+ run: ninja -C build clang-format
+
+ - name: Create patch
if: ${{ failure() && github.event_name == 'pull_request' }}
run: |
- echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style).' > clang-format.patch
+ echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch
+ echo '' >> clang-format.patch
+ echo 'clang-format.patch
' >> clang-format.patch
+ echo >> clang-format.patch
+ echo '```diff' >> clang-format.patch
+ git diff >> clang-format.patch
+ echo '```' >> clang-format.patch
+ echo >> clang-format.patch
+ echo ' ' >> clang-format.patch
- - name: Post comment
+ - name: Comment patch
if: ${{ failure() && github.event_name == 'pull_request' }}
uses: mshick/add-pr-comment@v2
with:
diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml
deleted file mode 100644
index 55f4e126..00000000
--- a/.github/workflows/close-issues.yml
+++ /dev/null
@@ -1,101 +0,0 @@
-name: Close Unauthorized Issues
-
-on:
- workflow_dispatch:
- issues:
- types: [opened]
-
-jobs:
- close-unauthorized-issues:
- runs-on: ubuntu-latest
- permissions:
- issues: write
- steps:
- # XXX: This *could* be done in Bash by abusing GitHub's own tool to interact with its API
- # but that's too much of a hack, and we'll be adding a layer of abstraction. github-script
- # is a workflow that eases interaction with GitHub API in the workflow run context.
- - name: "Close 'unauthorized' issues"
- uses: actions/github-script@v7
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const ALLOWED_USERS = ['vaxerski', 'fufexan', 'NotAShelf'];
- const CLOSING_COMMENT = 'Users are no longer allowed to open issues themselves, please open a discussion instead.\n\nPlease see the [wiki](https://wiki.hyprland.org/Contributing-and-Debugging/Issue-Guidelines/) on why this is the case.\n\nWe are volunteers, and we need your cooperation to make the best software we can. Thank you for understanding! ❤️\n\n[Open a discussion here](https://github.com/hyprwm/Hyprland/discussions)';
-
- async function closeUnauthorizedIssue(issueNumber, userName) {
- if (ALLOWED_USERS.includes(userName)) {
- console.log(`Issue #${issueNumber} - Created by authorized user ${userName}`);
- return;
- }
-
- console.log(`Issue #${issueNumber} - Unauthorized, closing`);
-
- await github.rest.issues.update({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: issueNumber,
- state: 'closed',
- state_reason: 'not_planned'
- });
-
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: issueNumber,
- body: CLOSING_COMMENT
- });
- }
-
- if (context.eventName === 'issues' && context.payload.action === 'opened') {
- // Direct access to the issue that triggered the workflow
- const issue = context.payload.issue;
-
- // Skip if this is a PR
- if (issue.pull_request) {
- console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
- return;
- }
-
- // Process the single issue that triggered the workflow
- await closeUnauthorizedIssue(issue.number, issue.user.login);
- } else {
- // For manual runs, we need to handle pagination
- async function* fetchAllOpenIssues() {
- let page = 1;
- let hasNextPage = true;
-
- while (hasNextPage) {
- const response = await github.rest.issues.listForRepo({
- owner: context.repo.owner,
- repo: context.repo.repo,
- state: 'open',
- per_page: 100,
- page: page
- });
-
- if (response.data.length === 0) {
- hasNextPage = false;
- } else {
- for (const issue of response.data) {
- yield issue;
- }
- page++;
- }
- }
- }
-
- // Process issues one by one
- for await (const issue of fetchAllOpenIssues()) {
- try {
- // Skip pull requests
- if (issue.pull_request) {
- console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
- continue;
- }
-
- await closeUnauthorizedIssue(issue.number, issue.user.login);
- } catch (error) {
- console.error(`Error processing issue #${issue.number}: ${error.message}`);
- }
- }
- }
diff --git a/.github/workflows/new-pr-comment.yml b/.github/workflows/new-pr-comment.yml
deleted file mode 100644
index 36ea1909..00000000
--- a/.github/workflows/new-pr-comment.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: "New MR welcome comment"
-
-on:
- pull_request_target:
- types:
- - opened
-
-jobs:
- comment:
- if: >
- github.event.pull_request.user.login != 'vaxerski' &&
- github.event.pull_request.user.login != 'fufexan' &&
- github.event.pull_request.user.login != 'gulafaran' &&
- github.event.pull_request.user.login != 'ujint34' &&
- github.event.pull_request.user.login != 'paideiadilemma' &&
- github.event.pull_request.user.login != 'notashelf'
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
-
- env:
- PR_COMMENT: |
- Hello and thank you for making a PR to Hyprland!
-
- Please check the [PR Guidelines](https://wiki.hypr.land/Contributing-and-Debugging/PR-Guidelines/) and make sure your PR follows them.
- It will make the entire review process faster. :)
-
- If your code can be tested, please always add tests. See more [here](https://wiki.hypr.land/Contributing-and-Debugging/Tests/).
-
- _beep boop, I'm just a bot. A real human will review your PR soon._
-
- steps:
- - name: Add comment to PR
- uses: actions/github-script@v7
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const pr = context.payload.pull_request;
-
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: pr.number,
- body: process.env.PR_COMMENT,
- });
diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml
new file mode 100644
index 00000000..5b2e81cf
--- /dev/null
+++ b/.github/workflows/nix-build.yml
@@ -0,0 +1,28 @@
+name: Nix (Build)
+
+on:
+ workflow_call:
+ secrets:
+ CACHIX_AUTH_TOKEN:
+ required: false
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ package:
+ - hyprland
+ # - hyprland-cross # cross compiling fails due to qt
+ # failure chain: hyprland-qtutils -> qt6.qtsvg -> qt6.qtbase -> psqlodbc & qt6.qttranslations
+ - xdg-desktop-portal-hyprland
+
+ runs-on: ubuntu-latest
+ steps:
+ - uses: DeterminateSystems/nix-installer-action@main
+
+ - uses: cachix/cachix-action@v15
+ with:
+ name: hyprland
+ authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
+
+ - run: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#${{ matrix.package }}' -L --extra-substituters "https://hyprland.cachix.org"
diff --git a/.github/workflows/nix-ci.yml b/.github/workflows/nix-ci.yml
index 5b22e992..75c19790 100644
--- a/.github/workflows/nix-ci.yml
+++ b/.github/workflows/nix-ci.yml
@@ -1,4 +1,4 @@
-name: Nix
+name: Nix (CI)
on: [push, pull_request, workflow_dispatch]
@@ -8,22 +8,7 @@ jobs:
uses: ./.github/workflows/nix-update-inputs.yml
secrets: inherit
- hyprland:
+ build:
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
- uses: ./.github/workflows/nix.yml
- secrets: inherit
- with:
- command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}' -L --extra-substituters "https://hyprland.cachix.org"
-
- xdph:
- if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
- needs: hyprland
- uses: ./.github/workflows/nix.yml
- secrets: inherit
- with:
- command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#xdg-desktop-portal-hyprland' -L --extra-substituters "https://hyprland.cachix.org"
-
- test:
- if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
- uses: ./.github/workflows/nix-test.yml
+ uses: ./.github/workflows/nix-build.yml
secrets: inherit
diff --git a/.github/workflows/nix-test.yml b/.github/workflows/nix-test.yml
deleted file mode 100644
index 68357093..00000000
--- a/.github/workflows/nix-test.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: Nix (Test)
-
-on:
- workflow_call:
- secrets:
- CACHIX_AUTH_TOKEN:
- required: false
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Install Nix
- uses: nixbuild/nix-quick-install-action@v31
- with:
- nix_conf: |
- keep-env-derivations = true
- keep-outputs = true
-
- - name: Restore and save Nix store
- uses: nix-community/cache-nix-action@v6
- with:
- # restore and save a cache using this key (per job)
- primary-key: nix-${{ runner.os }}-${{ github.job }}
- # if there's no cache hit, restore a cache by this prefix
- restore-prefixes-first-match: nix-${{ runner.os }}
- # collect garbage until the Nix store size (in bytes) is at most this number
- # before trying to save a new cache
- gc-max-store-size-linux: 5G
-
- - uses: cachix/cachix-action@v15
- with:
- name: hyprland
- authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
-
- - name: Run test VM
- run: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#checks.x86_64-linux.tests' -L --extra-substituters "https://hyprland.cachix.org"
-
- - name: Check exit status
- run: grep 0 result/exit_status
-
- - name: Upload artifacts
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: logs
- path: result
diff --git a/.github/workflows/nix-update-inputs.yml b/.github/workflows/nix-update-inputs.yml
index a3084b27..c9a094a2 100644
--- a/.github/workflows/nix-update-inputs.yml
+++ b/.github/workflows/nix-update-inputs.yml
@@ -17,24 +17,7 @@ jobs:
with:
token: ${{ secrets.PAT }}
- - name: Install Nix
- uses: nixbuild/nix-quick-install-action@v31
- with:
- nix_conf: |
- keep-env-derivations = true
- keep-outputs = true
-
- - name: Restore and save Nix store
- uses: nix-community/cache-nix-action@v6
- with:
- # restore and save a cache using this key (per job)
- primary-key: nix-${{ runner.os }}-${{ github.job }}
- # if there's no cache hit, restore a cache by this prefix
- restore-prefixes-first-match: nix-${{ runner.os }}
- # collect garbage until the Nix store size (in bytes) is at most this number
- # before trying to save a new cache
- gc-max-store-size-linux: 5G
-
+ - uses: DeterminateSystems/nix-installer-action@main
- name: Update inputs
run: nix/update-inputs.sh
diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml
deleted file mode 100644
index b46b3795..00000000
--- a/.github/workflows/nix.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: Build
-
-on:
- workflow_call:
- inputs:
- command:
- required: true
- type: string
- description: Command to run
- secrets:
- CACHIX_AUTH_TOKEN:
- required: false
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Install Nix
- uses: nixbuild/nix-quick-install-action@v31
- with:
- nix_conf: |
- keep-env-derivations = true
- keep-outputs = true
-
- - name: Restore and save Nix store
- uses: nix-community/cache-nix-action@v6
- with:
- # restore and save a cache using this key (per job)
- primary-key: nix-${{ runner.os }}-${{ github.job }}
- # if there's no cache hit, restore a cache by this prefix
- restore-prefixes-first-match: nix-${{ runner.os }}
- # collect garbage until the Nix store size (in bytes) is at most this number
- # before trying to save a new cache
- gc-max-store-size-linux: 5G
-
- - uses: cachix/cachix-action@v15
- with:
- name: hyprland
- authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
-
- - run: ${{ inputs.command }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 09aae111..44baff97 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -9,36 +9,17 @@ jobs:
source-tarball:
runs-on: ubuntu-latest
steps:
- - name: Checkout repository
- uses: actions/checkout@v5
+ - name: Checkout Hyprland
+ id: checkout
+ uses: actions/checkout@v4
with:
- fetch-depth: 0
submodules: recursive
- - name: Populate git info in version.h.in
+ - name: Generate version
+ id: genversion
run: |
- git fetch --tags --unshallow || true
-
- COMMIT_HASH=$(git rev-parse HEAD)
- BRANCH="${GITHUB_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}"
- COMMIT_MSG=$(git show -s --format=%s | sed 's/[&/]/\\&/g')
- COMMIT_DATE=$(git show -s --format=%cd --date=local)
- GIT_DIRTY=$(git diff-index --quiet HEAD -- && echo "clean" || echo "dirty")
- GIT_TAG=$(git describe --tags --always || echo "unknown")
- GIT_COMMITS=$(git rev-list --count HEAD)
-
- echo "Branch: $BRANCH"
- echo "Tag: $GIT_TAG"
-
- sed -i \
- -e "s|@GIT_COMMIT_HASH@|$COMMIT_HASH|" \
- -e "s|@GIT_BRANCH@|$BRANCH|" \
- -e "s|@GIT_COMMIT_MESSAGE@|$COMMIT_MSG|" \
- -e "s|@GIT_COMMIT_DATE@|$COMMIT_DATE|" \
- -e "s|@GIT_DIRTY@|$GIT_DIRTY|" \
- -e "s|@GIT_TAG@|$GIT_TAG|" \
- -e "s|@GIT_COMMITS@|$GIT_COMMITS|" \
- src/version.h.in
+ git fetch --unshallow || echo "failed unshallowing"
+ bash -c scripts/generateVersion.sh
- name: Create tarball with submodules
id: tar
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 00000000..9d2eed80
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,28 @@
+# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
+#
+# You can adjust the behavior by modifying this file.
+# For more information, see:
+# https://github.com/actions/stale
+name: Mark stale issues and pull requests
+
+on:
+ schedule:
+ - cron: "7 */4 * * *"
+ workflow_dispatch:
+
+jobs:
+ stale:
+ if: github.repository == 'hyprwm/Hyprland'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/stale@v9
+ with:
+ repo-token: ${{ secrets.STALEBOT_PAT }}
+ stale-issue-label: "stale"
+ stale-pr-label: "stale"
+ operations-per-run: 40
+ days-before-close: -1
diff --git a/.github/workflows/translation-ai-check.yml b/.github/workflows/translation-ai-check.yml
deleted file mode 100644
index d6a62a60..00000000
--- a/.github/workflows/translation-ai-check.yml
+++ /dev/null
@@ -1,139 +0,0 @@
-name: AI Translation Check
-
-on:
- # pull_request_target:
- # types:
- # - opened
- issue_comment:
- types:
- - created
-
-permissions:
- contents: read
- pull-requests: write
- issues: write
-
-jobs:
- review:
- name: Review Translation
- if: ${{ github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && github.event.action == 'created' && github.event.issue.pull_request != null && github.event.comment.user.login == 'vaxerski' && github.event.comment.body == 'ai, please recheck' ) }}
- runs-on: ubuntu-latest
- env:
- OPENAI_MODEL: gpt-5-mini
- SYSTEM_PROMPT: |
- You are a programmer and a translator. Your job is to review the attached patch for adding translation to a piece of software and make sure the submitted translation is not malicious, and that it makes sense. If the translation is not malicious, and doesn't contain obvious grammatical mistakes, say "Translation check OK". Otherwise, say "Translation check not ok" and list bad entries.
- Examples of bad translations include obvious trolling (slurs, etc) or nonsense sentences. Meaningful improvements may be suggested, but if there are only minor improvements, just reply with "Translation check OK". Do not provide anything but the result and (if applicable) the bad entries or improvements.
-
- AI_PROMPT: Translation patch below.
-
- steps:
- - name: Checkout source code
- uses: actions/checkout@v5
-
- - uses: dorny/paths-filter@v3
- id: changes
- with:
- filters: |
- i18n:
- - 'src/i18n/**'
-
- - name: Stop if i18n not changed
- if: steps.changes.outputs.i18n != 'true'
- run: echo "No i18n changes in this PR; skipping." && exit 0
-
- - name: Determine PR number
- id: pr
- run: |
- if [ "${{ github.event_name }}" = "pull_request_target" ]; then
- echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
- else
- echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
- fi
-
- - name: Download combined PR diff
- id: get_diff
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- PR_NUMBER: ${{ steps.pr.outputs.number }}
- run: |
- # Get the combined diff for the entire PR
- curl -sSL \
- -H "Authorization: token $GITHUB_TOKEN" \
- -H "Accept: application/vnd.github.v3.diff" \
- "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" \
- -o pr.diff
-
- # Compute character length
- LEN=$(wc -c < pr.diff | tr -d ' ')
- echo "len=$LEN" >> "$GITHUB_OUTPUT"
- if [ "$LEN" -gt 25000 ]; then
- echo "too_long=true" >> "$GITHUB_OUTPUT"
- else
- echo "too_long=false" >> "$GITHUB_OUTPUT"
- fi
-
- echo "got diff:"
- cat pr.diff
-
- - name: Comment when diff length exceeded
- if: steps.get_diff.outputs.too_long == 'true'
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- PR_NUMBER: ${{ steps.pr.outputs.number }}
- run: |
- jq -n --arg body "Diff length exceeded, can't query API" '{body: ("AI translation check result:\n\n" + $body)}' > body.json
- curl -sS -X POST \
- -H "Authorization: token $GITHUB_TOKEN" \
- -H "Content-Type: application/json" \
- "https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
- --data @body.json
-
- - name: Query OpenAI and post review
- if: steps.get_diff.outputs.too_long == 'false'
- env:
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- OPENAI_MODEL: ${{ env.OPENAI_MODEL }}
- SYSTEM_PROMPT: ${{ env.SYSTEM_PROMPT }}
- AI_PROMPT: ${{ env.AI_PROMPT }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- PR_NUMBER: ${{ steps.pr.outputs.number }}
- run: |
- # Prepare OpenAI chat request payload (embed diff safely)
- jq -n \
- --arg model "$OPENAI_MODEL" \
- --arg sys "$SYSTEM_PROMPT" \
- --arg prompt "$AI_PROMPT" \
- --rawfile diff pr.diff \
- '{model:$model,
- messages:[
- {role:"system", content:$sys},
- {role:"user", content: ($prompt + "\n\n```diff\n" + $diff + "\n```")}
- ]
- }' > payload.json
-
- # Call OpenAI
- curl -sS https://api.openai.com/v1/chat/completions \
- -H "Authorization: Bearer $OPENAI_API_KEY" \
- -H "Content-Type: application/json" \
- -d @payload.json > response.json
-
- # Extract response text
- COMMENT=$(jq -r '.choices[0].message.content // empty' response.json)
- if [ -z "$COMMENT" ]; then
- COMMENT="AI did not return a response."
- fi
-
- # If failed, add a note
- ADDITIONAL_NOTE=""
- if [[ "$COMMENT" == *"not ok"* ]]; then
- ADDITIONAL_NOTE=$(echo -ne "\n\nPlease note this check is a guideline, not a hard requirement. It is here to help you translate. If you disagree with some points, just state that. Any typos should be fixed.")
- fi
-
- # Post the review as a PR comment
- jq -n --arg body "$COMMENT" --arg note "$ADDITIONAL_NOTE" '{body: ("AI translation check result:\n\n" + $body + $note)}' > body.json
- echo "CURLing https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments"
- curl -sS -X POST \
- -H "Authorization: token $GITHUB_TOKEN" \
- -H "Content-Type: application/json" \
- "https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
- --data @body.json
diff --git a/.gitignore b/.gitignore
index 4e5c2323..a7934790 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,12 +28,8 @@ protocols/*.c*
protocols/*.h*
.ccls-cache
*.so
-src/render/shaders/*.inc
-src/render/shaders/Shaders.hpp
hyprctl/hyprctl
-hyprctl/hw-protocols/*.c*
-hyprctl/hw-protocols/*.h*
gmon.out
*.out
@@ -44,7 +40,6 @@ PKGBUILD
src/version.h
hyprpm/Makefile
hyprctl/Makefile
-example/hyprland.desktop
**/.#*.*
**/#*.*#
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87574b82..c4ad4ca0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,7 +9,6 @@ project(
DESCRIPTION "A Modern C++ Wayland Compositor"
VERSION ${VER})
-include(CTest)
include(CheckIncludeFile)
include(GNUInstallDirs)
@@ -17,21 +16,15 @@ set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX})
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
set(BINDIR ${CMAKE_INSTALL_BINDIR})
+configure_file(hyprland.pc.in hyprland.pc @ONLY)
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
message(STATUS "Gathering git info")
-# Make shader files includable
-execute_process(COMMAND ./scripts/generateShaderIncludes.sh
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
- RESULT_VARIABLE HYPR_SHADER_GEN_RESULT)
-if(NOT HYPR_SHADER_GEN_RESULT EQUAL 0)
- message(
- FATAL_ERROR
- "Failed to generate shader includes (scripts/generateShaderIncludes.sh), exit code: ${HYPR_SHADER_GEN_RESULT}"
- )
-endif()
+# Get git info hash and branch
+execute_process(COMMAND ./scripts/generateVersion.sh
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
find_package(PkgConfig REQUIRED)
@@ -39,23 +32,11 @@ find_package(PkgConfig REQUIRED)
# provide a .pc file and won't be detected this way
pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2)
-# Find non-pkgconfig udis86, otherwise fallback to subproject
+# Fallback to subproject
if(NOT udis_dep_FOUND)
- find_library(udis_nopc udis86)
- if(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
- message(STATUS "Found udis86 at ${udis_nopc}")
- else()
- add_subdirectory("subprojects/udis86")
- include_directories("subprojects/udis86")
- message(STATUS "udis86 dependency not found, falling back to subproject")
- endif()
-endif()
-
-find_library(librt rt)
-if("${librt}" MATCHES "librt-NOTFOUND")
- unset(LIBRT)
-else()
- set(LIBRT rt)
+ add_subdirectory("subprojects/udis86")
+ include_directories("subprojects/udis86")
+ message(STATUS "udis86 dependency not found, falling back to subproject")
endif()
if(CMAKE_BUILD_TYPE)
@@ -86,11 +67,9 @@ message(
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Configuring Hyprland in Debug with CMake")
add_compile_definitions(HYPRLAND_DEBUG)
- set(BUILD_TESTING ON)
else()
add_compile_options(-O3)
message(STATUS "Configuring Hyprland in Release with CMake")
- set(BUILD_TESTING OFF)
endif()
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
@@ -102,20 +81,13 @@ set(CXX_STANDARD_REQUIRED ON)
add_compile_options(
-Wall
-Wextra
- -Wpedantic
-Wno-unused-parameter
-Wno-unused-value
-Wno-missing-field-initializers
- -Wno-gnu-zero-variadic-macro-arguments
-Wno-narrowing
-Wno-pointer-arith
- -Wno-clobbered
- -frtti
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
-# disable lto as it may break plugins
-add_compile_options(-fno-lto)
-
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
@@ -123,158 +95,55 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
-set(GLES_VERSION "GLES3")
+if(LEGACY_RENDERER)
+ set(GLES_VERSION "GLES2")
+else()
+ set(GLES_VERSION "GLES3")
+endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
-find_package(glslang CONFIG REQUIRED)
-set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
-set(HYPRLANG_MINIMUM_VERSION 0.6.7)
-set(HYPRCURSOR_MINIMUM_VERSION 0.1.7)
-set(HYPRUTILS_MINIMUM_VERSION 0.11.0)
-set(HYPRGRAPHICS_MINIMUM_VERSION 0.1.6)
-
-pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION})
-pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=${HYPRLANG_MINIMUM_VERSION})
-pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=${HYPRCURSOR_MINIMUM_VERSION})
-pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=${HYPRUTILS_MINIMUM_VERSION})
-pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=${HYPRGRAPHICS_MINIMUM_VERSION})
+pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0)
+pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
+pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
+pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1)
+pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1)
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR)
list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH)
-set(AQUAMARINE_VERSION "${aquamarine_dep_VERSION}")
-set(AQUAMARINE_VERSION_MAJOR "${AQ_VERSION_MAJOR}")
-set(AQUAMARINE_VERSION_MINOR "${AQ_VERSION_MINOR}")
-set(AQUAMARINE_VERSION_PATCH "${AQ_VERSION_PATCH}")
-set(HYPRLANG_VERSION "${hyprlang_dep_VERSION}")
-set(HYPRUTILS_VERSION "${hyprutils_dep_VERSION}")
-set(HYPRCURSOR_VERSION "${hyprcursor_dep_VERSION}")
-set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}")
-
-
-find_package(Git QUIET)
-
-# Populate variables with env vars if present
-set(GIT_COMMIT_HASH "$ENV{GIT_COMMIT_HASH}")
-if(NOT GIT_COMMIT_HASH)
- set(GIT_COMMIT_HASH "unknown")
-endif()
-
-set(GIT_BRANCH "$ENV{GIT_BRANCH}")
-if(NOT GIT_BRANCH)
- set(GIT_BRANCH "unknown")
-endif()
-
-set(GIT_COMMIT_MESSAGE "$ENV{GIT_COMMIT_MESSAGE}")
-if(NOT GIT_COMMIT_MESSAGE)
- set(GIT_COMMIT_MESSAGE "unknown")
-endif()
-
-set(GIT_COMMIT_DATE "$ENV{GIT_COMMIT_DATE}")
-if(NOT GIT_COMMIT_DATE)
- set(GIT_COMMIT_DATE "unknown")
-endif()
-
-set(GIT_DIRTY "$ENV{GIT_DIRTY}")
-if(NOT GIT_DIRTY)
- set(GIT_DIRTY "unknown")
-endif()
-
-set(GIT_TAG "$ENV{GIT_TAG}")
-if(NOT GIT_TAG)
- set(GIT_TAG "unknown")
-endif()
-
-set(GIT_COMMITS "$ENV{GIT_COMMITS}")
-if(NOT GIT_COMMITS)
- set(GIT_COMMITS "0")
-endif()
-
-if(Git_FOUND)
- execute_process(
- COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
- OUTPUT_VARIABLE GIT_TOPLEVEL
- OUTPUT_STRIP_TRAILING_WHITESPACE
- ERROR_QUIET
- RESULT_VARIABLE GIT_TOPLEVEL_RESULT
- )
-
- if(GIT_TOPLEVEL_RESULT EQUAL 0)
- message(STATUS "Detected git repository root: ${GIT_TOPLEVEL}")
-
- execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(COMMAND sh "-c" "${GIT_EXECUTABLE} show -s --format=%s --no-show-signature | sed \"s/\\\"/\'/g\""
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local --no-show-signature
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD --
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- RESULT_VARIABLE GIT_DIRTY_RESULT)
- if(NOT GIT_DIRTY_RESULT EQUAL 0)
- set(GIT_DIRTY "dirty")
- else()
- set(GIT_DIRTY "clean")
- endif()
- execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE)
- execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
- WORKING_DIRECTORY ${GIT_TOPLEVEL}
- OUTPUT_VARIABLE GIT_COMMITS OUTPUT_STRIP_TRAILING_WHITESPACE)
- else()
- message(WARNING "No Git repository detected in ${CMAKE_SOURCE_DIR}")
- endif()
-endif()
-
-configure_file(
- ${CMAKE_SOURCE_DIR}/src/version.h.in
- ${CMAKE_SOURCE_DIR}/src/version.h
- @ONLY
-)
-
-set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERATED TRUE)
-
-set(XKBCOMMON_MINIMUM_VERSION 1.11.0)
-set(WAYLAND_SERVER_MINIMUM_VERSION 1.22.91)
-set(WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION 1.45)
-set(LIBINPUT_MINIMUM_VERSION 1.28)
+add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
+add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR})
+add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR})
+add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH})
+add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
+add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
+add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
+add_compile_definitions(HYPRGRAPHICS_VERSION="${hyprgraphics_dep_VERSION}")
pkg_check_modules(
deps
REQUIRED
- IMPORTED_TARGET GLOBAL
- xkbcommon>=${XKBCOMMON_MINIMUM_VERSION}
+ IMPORTED_TARGET
+ xkbcommon
uuid
- wayland-server>=${WAYLAND_SERVER_MINIMUM_VERSION}
- wayland-protocols>=${WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION}
+ wayland-server>=1.22.90
+ wayland-protocols>=1.41
cairo
pango
pangocairo
pixman-1
xcursor
libdrm
- libinput>=${LIBINPUT_MINIMUM_VERSION}
+ libinput
gbm
gio-2.0
- re2
- muparser
- lcms2)
+ re2)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
file(GLOB_RECURSE SRCFILES "src/*.cpp")
-get_filename_component(FULL_MAIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ABSOLUTE)
-list(REMOVE_ITEM SRCFILES "${FULL_MAIN_PATH}")
set(TRACY_CPP_FILES "")
if(USE_TRACY)
@@ -282,14 +151,9 @@ if(USE_TRACY)
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
endif()
-add_library(hyprland_lib STATIC ${SRCFILES})
-add_executable(Hyprland src/main.cpp ${TRACY_CPP_FILES})
-target_link_libraries(Hyprland hyprland_lib)
+add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
-target_include_directories(hyprland_lib PUBLIC ${deps_INCLUDE_DIRS})
-target_include_directories(Hyprland PUBLIC ${deps_INCLUDE_DIRS})
-
-set(USE_GPROF OFF)
+set(USE_GPROF ON)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
@@ -297,8 +161,23 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
if(WITH_ASAN)
message(STATUS "Enabling ASan")
- target_link_libraries(hyprland_lib PUBLIC asan)
- target_compile_options(hyprland_lib PUBLIC -fsanitize=address)
+ target_link_libraries(Hyprland asan)
+ target_compile_options(Hyprland PUBLIC -fsanitize=address)
+ endif()
+
+ if(USE_TRACY)
+ message(STATUS "Tracy is turned on")
+
+ option(TRACY_ENABLE "" ON)
+ option(TRACY_ON_DEMAND "" ON)
+ add_subdirectory(subprojects/tracy)
+
+ target_link_libraries(Hyprland Tracy::TracyClient)
+
+ if(USE_TRACY_GPU)
+ message(STATUS "Tracy GPU Profiling is turned on")
+ add_compile_definitions(USE_TRACY_GPU)
+ endif()
endif()
add_compile_options(-fno-pie -fno-builtin)
@@ -309,27 +188,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
endif()
endif()
-if(USE_TRACY)
- message(STATUS "Tracy is turned on")
-
- option(TRACY_ENABLE "" ON)
- option(TRACY_ON_DEMAND "" ON)
- add_subdirectory(subprojects/tracy)
-
- add_compile_options(-fno-omit-frame-pointer)
-
- target_link_libraries(hyprland_lib PUBLIC Tracy::TracyClient)
-
- if(USE_TRACY_GPU)
- message(STATUS "Tracy GPU Profiling is turned on")
- add_compile_definitions(USE_TRACY_GPU)
- endif()
-endif()
-
-if(BUILT_WITH_NIX)
- add_compile_definitions(BUILT_WITH_NIX)
-endif()
-
check_include_file("execinfo.h" EXECINFOH)
if(EXECINFOH)
message(STATUS "Configuration supports execinfo")
@@ -339,19 +197,24 @@ endif()
include(CheckLibraryExists)
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
if(HAVE_LIBEXECINFO)
- target_link_libraries(hyprland_lib PUBLIC execinfo)
+ target_link_libraries(Hyprland execinfo)
endif()
check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
if(NOT HAS_TIMERFD AND epoll_FOUND)
- target_link_libraries(hyprland_lib PUBLIC PkgConfig::epoll)
+ target_link_libraries(Hyprland PkgConfig::epoll)
endif()
check_include_file("sys/inotify.h" HAS_INOTIFY)
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
if(NOT HAS_INOTIFY AND inotify_FOUND)
- target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify)
+ target_link_libraries(Hyprland PkgConfig::inotify)
+endif()
+
+if(LEGACY_RENDERER)
+ message(STATUS "Using the legacy GLES2 renderer!")
+ add_compile_definitions(LEGACY_RENDERER)
endif()
if(NO_XWAYLAND)
@@ -359,7 +222,10 @@ if(NO_XWAYLAND)
add_compile_definitions(NO_XWAYLAND)
else()
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
- set(XWAYLAND_DEPENDENCIES
+ pkg_check_modules(
+ xdeps
+ REQUIRED
+ IMPORTED_TARGET
xcb
xcb-render
xcb-xfixes
@@ -367,21 +233,9 @@ else()
xcb-composite
xcb-res
xcb-errors)
-
- pkg_check_modules(
- xdeps
- REQUIRED
- IMPORTED_TARGET
- ${XWAYLAND_DEPENDENCIES})
-
- string(JOIN ", " PKGCONFIG_XWAYLAND_DEPENDENCIES ${XWAYLAND_DEPENDENCIES})
- string(PREPEND PKGCONFIG_XWAYLAND_DEPENDENCIES ", ")
-
- target_link_libraries(hyprland_lib PUBLIC PkgConfig::xdeps)
+ target_link_libraries(Hyprland PkgConfig::xdeps)
endif()
-configure_file(hyprland.pc.in hyprland.pc @ONLY)
-
if(NO_SYSTEMD)
message(STATUS "SYSTEMD support is disabled...")
else()
@@ -402,42 +256,30 @@ set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
-if(CMAKE_DISABLE_PRECOMPILE_HEADERS)
- message(STATUS "Not using precompiled headers")
-else()
- message(STATUS "Setting precompiled headers")
- target_precompile_headers(hyprland_lib PRIVATE
- $<$:src/pch/pch.hpp>)
-endif()
+message(STATUS "Setting precompiled headers")
+
+target_precompile_headers(Hyprland PRIVATE
+ $<$:src/pch/pch.hpp>)
message(STATUS "Setting link libraries")
target_link_libraries(
- hyprland_lib
- PUBLIC
+ Hyprland
+ rt
PkgConfig::aquamarine_dep
PkgConfig::hyprlang_dep
PkgConfig::hyprutils_dep
PkgConfig::hyprcursor_dep
PkgConfig::hyprgraphics_dep
- PkgConfig::deps
-)
-
-target_link_libraries(
- Hyprland
- ${LIBRT}
- hyprland_lib)
+ PkgConfig::deps)
if(udis_dep_FOUND)
- target_link_libraries(hyprland_lib PUBLIC PkgConfig::udis_dep)
-elseif(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
- target_link_libraries(hyprland_lib PUBLIC ${udis_nopc})
+ target_link_libraries(Hyprland PkgConfig::udis_dep)
else()
- target_link_libraries(hyprland_lib PUBLIC libudis86)
+ target_link_libraries(Hyprland libudis86)
endif()
# used by `make installheaders`, to ensure the headers are generated
add_custom_target(generate-protocol-headers)
-set(PROTOCOL_SOURCES "")
function(protocolnew protoPath protoName external)
if(external)
@@ -451,15 +293,10 @@ function(protocolnew protoPath protoName external)
COMMAND hyprwayland-scanner ${path}/${protoName}.xml
${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- target_sources(hyprland_lib PRIVATE protocols/${protoName}.cpp
+ target_sources(Hyprland PRIVATE protocols/${protoName}.cpp
protocols/${protoName}.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
-
- list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp")
- set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
- list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp")
- set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
endfunction()
function(protocolWayland)
add_custom_command(
@@ -469,23 +306,14 @@ function(protocolWayland)
hyprwayland-scanner --wayland-enums
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
- target_sources(hyprland_lib PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
+ target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
-
- list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.hpp")
- set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
- list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.cpp")
- set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
endfunction()
-if(TARGET OpenGL::GL)
- target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
-else()
- target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
-endif()
+target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
-pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
+pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.2)
if(hyprland_protocols_dep_FOUND)
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
@@ -511,12 +339,12 @@ protocolnew("protocols" "kde-server-decoration" true)
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
+protocolnew("protocols" "xx-color-management-v4" true)
+protocolnew("protocols" "frog-color-management-v1" true)
protocolnew("protocols" "wayland-drm" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
-protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1"
- true)
protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
@@ -551,21 +379,11 @@ protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
protocolnew("staging/security-context" "security-context-v1" false)
protocolnew("staging/content-type" "content-type-v1" false)
protocolnew("staging/color-management" "color-management-v1" false)
-protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false)
-protocolnew("staging/xdg-system-bell" "xdg-system-bell-v1" false)
-protocolnew("staging/ext-workspace" "ext-workspace-v1" false)
-protocolnew("staging/ext-data-control" "ext-data-control-v1" false)
-protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
-protocolnew("staging/fifo" "fifo-v1" false)
-protocolnew("staging/commit-timing" "commit-timing-v1" false)
-protocolnew("staging/ext-image-capture-source" "ext-image-capture-source-v1" false)
-protocolnew("staging/ext-image-copy-capture" "ext-image-copy-capture-v1" false)
protocolwayland()
# tools
add_subdirectory(hyprctl)
-add_subdirectory(start)
if(NO_HYPRPM)
message(STATUS "hyprpm is disabled")
@@ -584,11 +402,6 @@ install(
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
)")
# session file
-configure_file(
- ${CMAKE_SOURCE_DIR}/example/hyprland.desktop.in
- ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
- @ONLY
-)
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
@@ -597,6 +410,7 @@ add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
# installable assets
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
+list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
install(FILES ${INSTALLABLE_ASSETS}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
@@ -630,51 +444,5 @@ install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING
- PATTERN "*.h"
- PATTERN "*.hpp"
- PATTERN "*.inc")
-
-if(BUILD_TESTING OR WITH_TESTS)
- message(STATUS "Building tests")
-
- # hyprtester
- add_subdirectory(hyprtester)
-
- # GTest
- find_package(GTest CONFIG REQUIRED)
- include(GoogleTest)
- file(GLOB_RECURSE TESTFILES "tests/*.cpp")
- add_executable(hyprland_gtests ${TESTFILES})
- target_compile_options(hyprland_gtests PRIVATE --coverage)
- target_link_options(hyprland_gtests PRIVATE --coverage)
- target_include_directories(
- hyprland_gtests
- PUBLIC "./include"
- PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
-
- target_link_libraries(hyprland_gtests hyprland_lib GTest::gtest_main)
-
- gtest_discover_tests(hyprland_gtests)
-
- # Enable coverage in main hyprland lib
- target_compile_options(hyprland_lib PRIVATE --coverage)
- target_link_options(hyprland_lib PRIVATE --coverage)
- target_link_libraries(hyprland_lib PUBLIC gcov)
-
- # Enable coverage in hyprland exe
- target_compile_options(Hyprland PRIVATE --coverage)
- target_link_options(Hyprland PRIVATE --coverage)
- target_link_libraries(Hyprland gcov)
-endif()
-
-if(BUILD_TESTING)
- message(STATUS "Testing is enabled")
-
- enable_testing()
- add_custom_target(tests)
-
- add_dependencies(tests hyprland_gtests)
-
-else()
- message(STATUS "Testing is disabled")
-endif()
+ PATTERN "*.h*"
+ PATTERN "*.frag")
diff --git a/LICENSE b/LICENSE
index efdec21a..a34afebb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2022-2026, vaxerski
+Copyright (c) 2022-2024, vaxerski
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
index 852fcddf..6c79b8db 100644
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,20 @@ PREFIX = /usr/local
stub:
@echo "Do not run $(MAKE) directly without any arguments. Please refer to the wiki on how to compile Hyprland."
+legacyrenderer:
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
+ cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
+
+legacyrendererdebug:
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
+ cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
+
release:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
debug:
- cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DTESTS=true -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
nopch:
@@ -18,7 +26,6 @@ nopch:
clear:
rm -rf build
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
- rm -f ./hyprctl/hw-protocols/*.cpp ./hyprctl/hw-protocols/*.hpp
all:
$(MAKE) clear
@@ -45,7 +52,7 @@ installheaders:
cmake --build ./build --config Release --target generate-protocol-headers
- find src -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.inc' \) -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
+ find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
@@ -88,12 +95,8 @@ asan:
@echo "Wayland done"
patch -p1 < ./scripts/hyprlandStaticAsan.diff
- cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build
+ cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all
@echo "Hyprland done"
ASAN_OPTIONS="detect_odr_violation=0,log_path=asan.log" HYPRLAND_NO_CRASHREPORTER=1 ./build/Hyprland -c ~/.config/hypr/hyprland.conf
-
-test:
- $(MAKE) debug
- ./build/hyprtester/hyprtester -c hyprtester/test.conf -b ./build/Hyprland -p hyprtester/plugin/hyprtestplugin.so
diff --git a/README.md b/README.md
index bb74c4e4..0970819c 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-[![Badge Workflow]][Workflow]
+![Badge Workflow]
[![Badge License]][License]
![Badge Language]
[![Badge Pull Requests]][Pull Requests]
@@ -100,7 +100,7 @@ easy IPC, much more QoL stuff than other compositors and more...
-[Configure]: https://wiki.hypr.land/Configuring/
+[Configure]: https://wiki.hyprland.org/Configuring/
[Stars]: https://starchart.cc/hyprwm/Hyprland
[Hypr]: https://github.com/hyprwm/Hypr
@@ -108,10 +108,9 @@ easy IPC, much more QoL stuff than other compositors and more...
[Issues]: https://github.com/hyprwm/Hyprland/issues
[Todo]: https://github.com/hyprwm/Hyprland/projects?type=beta
-[Contribute]: https://wiki.hypr.land/Contributing-and-Debugging/
-[Install]: https://wiki.hypr.land/Getting-Started/Installation/
-[Quick Start]: https://wiki.hypr.land/Getting-Started/Master-Tutorial/
-[Workflow]: https://github.com/hyprwm/Hyprland/actions/workflows/ci.yaml
+[Contribute]: https://wiki.hyprland.org/Contributing-and-Debugging/
+[Install]: https://wiki.hyprland.org/Getting-Started/Installation/
+[Quick Start]: https://wiki.hyprland.org/Getting-Started/Master-Tutorial/
[License]: LICENSE
@@ -126,9 +125,9 @@ easy IPC, much more QoL stuff than other compositors and more...
-[Preview A]: https://i.ibb.co/XxFY75Mk/greerggergerhtrytghjnyhjn.png
-[Preview B]: https://i.ibb.co/C1yTb0r/falf.png
-[Preview C]: https://i.ibb.co/2Yc4q835/hyprland-preview-b.png
+[Preview A]: https://i.ibb.co/C1yTb0r/falf.png
+[Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.png
+[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 187165ce..00000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Hyprland Development Security Policy
-
-If you have a bug that affects the security of your system, you may
-want to privately disclose it instead of making it immediately public.
-
-## Supported versions
-
-_Only_ the most recent release on Github is supported. There are no LTS releases.
-
-## What is not a security issue
-
-Some examples of issues that should not be reported as security issues:
-
-- An app can execute a command when ran outside of a sandbox
-- An app can write / read hyprland sockets when ran outside of a sandbox
-- Crashes
-- Things that are protected via permissions when the permission system is disabled
-
-## What is a security issue
-
-Some examples of issues that should be reported as security issues:
-
-- Sandboxed application executing arbitrary code via Hyprland
-- Application being able to modify Hyprland's code on the fly
-- Application being able to keylog / track user's activity beyond what the wayland protocols allow
-
-## How to report security issues
-
-Please report your security issues via either of these channels:
-- Mail: `vaxry [at] vaxry [dot] net`
-- Matrix: `@vaxry:matrix.vaxry.net`
-- Discord: `@vaxry`
diff --git a/VERSION b/VERSION
index 524456c7..a758a09a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.54.0
+0.48.0
diff --git a/assets/header.svg b/assets/header.svg
index a2b32551..c8cf8222 100644
--- a/assets/header.svg
+++ b/assets/header.svg
@@ -1,64 +1,72 @@
-
\ No newline at end of file
+ @keyframes scaleit {
+ 0% {
+ transform: scale(110%);
+ }
+ 50% {
+ transform: scale(100%);
+ }
+ 100% {
+ transform: scale(110%);
+ }
+ }
+
+
+
+ Hyprland
+
+
+
+
diff --git a/assets/install/meson.build b/assets/install/meson.build
new file mode 100644
index 00000000..45076469
--- /dev/null
+++ b/assets/install/meson.build
@@ -0,0 +1,10 @@
+globber = run_command('sh', '-c', 'find . -type f -not -name "*.build"', check: true)
+files = globber.stdout().strip().split('\n')
+
+foreach file : files
+ install_data(
+ file,
+ install_dir: join_paths(get_option('datadir'), 'hypr'),
+ install_tag: 'runtime',
+ )
+endforeach
diff --git a/assets/meson.build b/assets/meson.build
new file mode 100644
index 00000000..2a28121d
--- /dev/null
+++ b/assets/meson.build
@@ -0,0 +1,7 @@
+install_data(
+ 'hyprland-portals.conf',
+ install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
+ install_tag: 'runtime',
+)
+
+subdir('install')
diff --git a/docs/Hyprland.1 b/docs/Hyprland.1
index 6a833508..2d6e1444 100644
--- a/docs/Hyprland.1
+++ b/docs/Hyprland.1
@@ -1,19 +1,5 @@
-.\" Automatically generated by Pandoc 3.1.3
+.\" Automatically generated by Pandoc 2.9.2.1
.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
.TH "Hyprland" "1" "" "" "Hyprland User Manual"
.hy
.SH NAME
diff --git a/docs/ISSUE_GUIDELINES.md b/docs/ISSUE_GUIDELINES.md
index 060b27a0..9328642d 100644
--- a/docs/ISSUE_GUIDELINES.md
+++ b/docs/ISSUE_GUIDELINES.md
@@ -2,15 +2,15 @@
First of all, please remember to:
- Check that your issue is not a duplicate
-- Read the [FAQ](https://wiki.hypr.land/FAQ/)
-- Read the [Configuring Page](https://wiki.hypr.land/Configuring/)
+- Read the [FAQ](https://wiki.hyprland.org/FAQ/)
+- Read the [Configuring Page](https://wiki.hyprland.org/Configuring/)
# Reporting suggestions
Suggestions are welcome.
-Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hypr.land/IPC). Please do not suggest features that can be implemented as such.
+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.
@@ -70,7 +70,7 @@ A debug coredump provides more information for debugging and may speed up the pr
Make sure you're on latest git. Run `git pull --recurse-submodules` to sync everything.
-1. [Compile Hyprland with debug mode](http://wiki.hypr.land/Contributing-and-Debugging/#build-in-debug-mode)
+1. [Compile Hyprland with debug mode](http://wiki.hyprland.org/Contributing-and-Debugging/#build-in-debug-mode)
> Note: The config file used will be `hyprlandd.conf` instead of `hyprland.conf`
2. `cd ~`
diff --git a/docs/hyprctl.1 b/docs/hyprctl.1
index 61cb84fd..8c9f3a23 100644
--- a/docs/hyprctl.1
+++ b/docs/hyprctl.1
@@ -1,19 +1,5 @@
-.\" Automatically generated by Pandoc 3.1.3
+.\" Automatically generated by Pandoc 2.9.2.1
.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
.TH "hyprctl" "1" "" "" "hyprctl User Manual"
.hy
.SH NAME
diff --git a/docs/meson.build b/docs/meson.build
new file mode 100644
index 00000000..6ff51d1a
--- /dev/null
+++ b/docs/meson.build
@@ -0,0 +1,2 @@
+install_man('Hyprland.1')
+install_man('hyprctl.1')
diff --git a/example/hyprland.conf b/example/hyprland.conf
index 15b0e4f7..cfe5040b 100644
--- a/example/hyprland.conf
+++ b/example/hyprland.conf
@@ -1,6 +1,6 @@
# This is an example Hyprland config file.
# Refer to the wiki for more information.
-# https://wiki.hypr.land/Configuring/
+# https://wiki.hyprland.org/Configuring/
# Please note not all available settings / options are set here.
# For a full list, see the wiki
@@ -14,7 +14,7 @@
### MONITORS ###
################
-# See https://wiki.hypr.land/Configuring/Monitors/
+# See https://wiki.hyprland.org/Configuring/Monitors/
monitor=,preferred,auto,auto
@@ -22,12 +22,12 @@ monitor=,preferred,auto,auto
### MY PROGRAMS ###
###################
-# See https://wiki.hypr.land/Configuring/Keywords/
+# See https://wiki.hyprland.org/Configuring/Keywords/
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
-$menu = hyprlauncher
+$menu = wofi --show drun
#################
@@ -46,56 +46,39 @@ $menu = hyprlauncher
### ENVIRONMENT VARIABLES ###
#############################
-# See https://wiki.hypr.land/Configuring/Environment-variables/
+# See https://wiki.hyprland.org/Configuring/Environment-variables/
env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24
-###################
-### PERMISSIONS ###
-###################
-
-# See https://wiki.hypr.land/Configuring/Permissions/
-# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
-# for security reasons
-
-# ecosystem {
-# enforce_permissions = 1
-# }
-
-# permission = /usr/(bin|local/bin)/grim, screencopy, allow
-# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
-# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
-
-
#####################
### LOOK AND FEEL ###
#####################
-# Refer to https://wiki.hypr.land/Configuring/Variables/
+# Refer to https://wiki.hyprland.org/Configuring/Variables/
-# https://wiki.hypr.land/Configuring/Variables/#general
+# https://wiki.hyprland.org/Configuring/Variables/#general
general {
gaps_in = 5
gaps_out = 20
border_size = 2
- # https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
+ # https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
col.inactive_border = rgba(595959aa)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
- # Please see https://wiki.hypr.land/Configuring/Tearing/ before you turn this on
+ # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
-# https://wiki.hypr.land/Configuring/Variables/#decoration
+# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
rounding = 10
rounding_power = 2
@@ -111,7 +94,7 @@ decoration {
color = rgba(1a1a1aee)
}
- # https://wiki.hypr.land/Configuring/Variables/#blur
+ # https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
enabled = true
size = 3
@@ -121,74 +104,58 @@ decoration {
}
}
-# https://wiki.hypr.land/Configuring/Variables/#animations
+# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = yes, please :)
- # Default curves, see https://wiki.hypr.land/Configuring/Animations/#curves
- # NAME, X0, Y0, X1, Y1
- bezier = easeOutQuint, 0.23, 1, 0.32, 1
- bezier = easeInOutCubic, 0.65, 0.05, 0.36, 1
- bezier = linear, 0, 0, 1, 1
- bezier = almostLinear, 0.5, 0.5, 0.75, 1
- bezier = quick, 0.15, 0, 0.1, 1
+ # Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
- # Default animations, see https://wiki.hypr.land/Configuring/Animations/
- # NAME, ONOFF, SPEED, CURVE, [STYLE]
- animation = global, 1, 10, default
- animation = border, 1, 5.39, easeOutQuint
- animation = windows, 1, 4.79, easeOutQuint
- animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
- animation = windowsOut, 1, 1.49, linear, popin 87%
- animation = fadeIn, 1, 1.73, almostLinear
- animation = fadeOut, 1, 1.46, almostLinear
- animation = fade, 1, 3.03, quick
- animation = layers, 1, 3.81, easeOutQuint
- animation = layersIn, 1, 4, easeOutQuint, fade
- animation = layersOut, 1, 1.5, linear, fade
- animation = fadeLayersIn, 1, 1.79, almostLinear
- animation = fadeLayersOut, 1, 1.39, almostLinear
- animation = workspaces, 1, 1.94, almostLinear, fade
- animation = workspacesIn, 1, 1.21, almostLinear, fade
- animation = workspacesOut, 1, 1.94, almostLinear, fade
- animation = zoomFactor, 1, 7, quick
+ bezier = easeOutQuint,0.23,1,0.32,1
+ bezier = easeInOutCubic,0.65,0.05,0.36,1
+ bezier = linear,0,0,1,1
+ bezier = almostLinear,0.5,0.5,0.75,1.0
+ bezier = quick,0.15,0,0.1,1
+
+ animation = global, 1, 10, default
+ animation = border, 1, 5.39, easeOutQuint
+ animation = windows, 1, 4.79, easeOutQuint
+ animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
+ animation = windowsOut, 1, 1.49, linear, popin 87%
+ animation = fadeIn, 1, 1.73, almostLinear
+ animation = fadeOut, 1, 1.46, almostLinear
+ animation = fade, 1, 3.03, quick
+ animation = layers, 1, 3.81, easeOutQuint
+ animation = layersIn, 1, 4, easeOutQuint, fade
+ animation = layersOut, 1, 1.5, linear, fade
+ animation = fadeLayersIn, 1, 1.79, almostLinear
+ animation = fadeLayersOut, 1, 1.39, almostLinear
+ animation = workspaces, 1, 1.94, almostLinear, fade
+ animation = workspacesIn, 1, 1.21, almostLinear, fade
+ animation = workspacesOut, 1, 1.94, almostLinear, fade
}
-# Ref https://wiki.hypr.land/Configuring/Workspace-Rules/
+# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
-# windowrule {
-# name = no-gaps-wtv1
-# match:float = false
-# match:workspace = w[tv1]
-#
-# border_size = 0
-# rounding = 0
-# }
-#
-# windowrule {
-# name = no-gaps-f1
-# match:float = false
-# match:workspace = f[1]
-#
-# border_size = 0
-# rounding = 0
-# }
+# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
+# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
+# windowrule = bordersize 0, floating:0, onworkspace:f[1]
+# windowrule = rounding 0, floating:0, onworkspace:f[1]
-# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
+# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = true # You probably want this
}
-# See https://wiki.hypr.land/Configuring/Master-Layout/ for more
+# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_status = master
}
-# https://wiki.hypr.land/Configuring/Variables/#misc
+# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
@@ -199,7 +166,7 @@ misc {
### INPUT ###
#############
-# https://wiki.hypr.land/Configuring/Variables/#input
+# https://wiki.hyprland.org/Configuring/Variables/#input
input {
kb_layout = us
kb_variant =
@@ -216,11 +183,13 @@ input {
}
}
-# See https://wiki.hypr.land/Configuring/Gestures
-gesture = 3, horizontal, workspace
+# https://wiki.hyprland.org/Configuring/Variables/#gestures
+gestures {
+ workspace_swipe = false
+}
# Example per-device config
-# See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more
+# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device {
name = epic-mouse-v1
sensitivity = -0.5
@@ -231,18 +200,18 @@ device {
### KEYBINDINGS ###
###################
-# See https://wiki.hypr.land/Configuring/Keywords/
+# See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier
-# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
+# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive,
-bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
+bind = $mainMod, M, exit,
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
-bind = $mainMod, J, layoutmsg, togglesplit # dwindle
+bind = $mainMod, J, togglesplit, # dwindle
# Move focus with mainMod + arrow keys
bind = $mainMod, left, movefocus, l
@@ -291,8 +260,8 @@ bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
-bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
-bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
+bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
+bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
@@ -304,38 +273,14 @@ bindl = , XF86AudioPrev, exec, playerctl previous
### WINDOWS AND WORKSPACES ###
##############################
-# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
-# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
+# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
+# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
-# Example windowrules that are useful
+# Example windowrule
+# windowrule = float,class:^(kitty)$,title:^(kitty)$
-windowrule {
- # Ignore maximize requests from all apps. You'll probably like this.
- name = suppress-maximize-events
- match:class = .*
+# Ignore maximize requests from apps. You'll probably like this.
+windowrule = suppressevent maximize, class:.*
- suppress_event = maximize
-}
-
-windowrule {
- # Fix some dragging issues with XWayland
- name = fix-xwayland-drags
- match:class = ^$
- match:title = ^$
- match:xwayland = true
- match:float = true
- match:fullscreen = false
- match:pin = false
-
- no_focus = true
-}
-
-# Hyprland-run windowrule
-windowrule {
- name = move-hyprland-run
-
- match:class = hyprland-run
-
- move = 20 monitor_h-120
- float = yes
-}
+# Fix some dragging issues with XWayland
+windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
diff --git a/example/hyprland.desktop.in b/example/hyprland.desktop
similarity index 75%
rename from example/hyprland.desktop.in
rename to example/hyprland.desktop
index d8e87d60..bb2801a9 100644
--- a/example/hyprland.desktop.in
+++ b/example/hyprland.desktop
@@ -1,7 +1,7 @@
[Desktop Entry]
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
-Exec=@PREFIX@/@CMAKE_INSTALL_BINDIR@/start-hyprland
+Exec=Hyprland
Type=Application
DesktopNames=Hyprland
Keywords=tiling;wayland;compositor;
diff --git a/example/meson.build b/example/meson.build
new file mode 100644
index 00000000..a338644e
--- /dev/null
+++ b/example/meson.build
@@ -0,0 +1,10 @@
+install_data(
+ 'hyprland.conf',
+ install_dir: join_paths(get_option('datadir'), 'hypr'),
+ install_tag: 'runtime',
+)
+install_data(
+ 'hyprland.desktop',
+ install_dir: join_paths(get_option('datadir'), 'wayland-sessions'),
+ install_tag: 'runtime',
+)
diff --git a/example/screenShader.frag b/example/screenShader.frag
index 71598e51..5eeac17a 100644
--- a/example/screenShader.frag
+++ b/example/screenShader.frag
@@ -1,19 +1,16 @@
//
// Example blue light filter shader.
-//
-
-#version 300 es
+//
precision mediump float;
-in vec2 v_texcoord;
-layout(location = 0) out vec4 fragColor;
+varying vec2 v_texcoord;
uniform sampler2D tex;
void main() {
- vec4 pixColor = texture(tex, v_texcoord);
+ vec4 pixColor = texture2D(tex, v_texcoord);
pixColor[2] *= 0.8;
- fragColor = pixColor;
+ gl_FragColor = pixColor;
}
diff --git a/flake.lock b/flake.lock
index 4a89c0fc..9dbd3bc8 100644
--- a/flake.lock
+++ b/flake.lock
@@ -16,11 +16,11 @@
]
},
"locked": {
- "lastModified": 1772292445,
- "narHash": "sha256-4F1Q7U313TKUDDovCC96m/Za4wZcJ3yqtu4eSrj8lk8=",
+ "lastModified": 1742213273,
+ "narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=",
"owner": "hyprwm",
"repo": "aquamarine",
- "rev": "1dbbba659c1cef0b0202ce92cadfe13bae550e8f",
+ "rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf",
"type": "github"
},
"original": {
@@ -32,15 +32,15 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1767039857,
- "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
- "owner": "NixOS",
+ "lastModified": 1696426674,
+ "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "owner": "edolstra",
"repo": "flake-compat",
- "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
- "owner": "NixOS",
+ "owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
@@ -79,11 +79,11 @@
]
},
"locked": {
- "lastModified": 1753964049,
- "narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=",
+ "lastModified": 1742215578,
+ "narHash": "sha256-zfs71PXVVPEe56WEyNi2TJQPs0wabU4WAlq0XV7GcdE=",
"owner": "hyprwm",
"repo": "hyprcursor",
- "rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5",
+ "rev": "2fd36421c21aa87e2fe3bee11067540ae612f719",
"type": "github"
},
"original": {
@@ -105,11 +105,11 @@
]
},
"locked": {
- "lastModified": 1770511807,
- "narHash": "sha256-suKmSbSk34uPOJDTg/GbPrKEJutzK08vj0VoTvAFBCA=",
+ "lastModified": 1739049071,
+ "narHash": "sha256-3+7TpXMrbsUXSwgr5VAKAnmkzMb6JO+Rvc9XRb5NMg4=",
"owner": "hyprwm",
"repo": "hyprgraphics",
- "rev": "7c75487edd43a71b61adb01cae8326d277aab683",
+ "rev": "175c6b29b6ff82100539e7c4363a35a02c74dd73",
"type": "github"
},
"original": {
@@ -118,45 +118,6 @@
"type": "github"
}
},
- "hyprland-guiutils": {
- "inputs": {
- "aquamarine": [
- "aquamarine"
- ],
- "hyprgraphics": [
- "hyprgraphics"
- ],
- "hyprlang": [
- "hyprlang"
- ],
- "hyprtoolkit": "hyprtoolkit",
- "hyprutils": [
- "hyprutils"
- ],
- "hyprwayland-scanner": [
- "hyprwayland-scanner"
- ],
- "nixpkgs": [
- "nixpkgs"
- ],
- "systems": [
- "systems"
- ]
- },
- "locked": {
- "lastModified": 1767023960,
- "narHash": "sha256-R2HgtVS1G3KSIKAQ77aOZ+Q0HituOmPgXW9nBNkpp3Q=",
- "owner": "hyprwm",
- "repo": "hyprland-guiutils",
- "rev": "c2e906261142f5dd1ee0bfc44abba23e2754c660",
- "type": "github"
- },
- "original": {
- "owner": "hyprwm",
- "repo": "hyprland-guiutils",
- "type": "github"
- }
- },
"hyprland-protocols": {
"inputs": {
"nixpkgs": [
@@ -167,11 +128,11 @@
]
},
"locked": {
- "lastModified": 1765214753,
- "narHash": "sha256-P9zdGXOzToJJgu5sVjv7oeOGPIIwrd9hAUAP3PsmBBs=",
+ "lastModified": 1738422629,
+ "narHash": "sha256-5v+bv75wJWvahyM2xcMTSNNxmV8a7hb01Eey5zYnBJw=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
- "rev": "3f3860b869014c00e8b9e0528c7b4ddc335c21ab",
+ "rev": "755aef8dab49d0fc4663c715fa4ad221b2aedaed",
"type": "github"
},
"original": {
@@ -180,6 +141,67 @@
"type": "github"
}
},
+ "hyprland-qt-support": {
+ "inputs": {
+ "hyprlang": [
+ "hyprland-qtutils",
+ "hyprlang"
+ ],
+ "nixpkgs": [
+ "hyprland-qtutils",
+ "nixpkgs"
+ ],
+ "systems": [
+ "hyprland-qtutils",
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1737634706,
+ "narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=",
+ "owner": "hyprwm",
+ "repo": "hyprland-qt-support",
+ "rev": "8810df502cdee755993cb803eba7b23f189db795",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprland-qt-support",
+ "type": "github"
+ }
+ },
+ "hyprland-qtutils": {
+ "inputs": {
+ "hyprland-qt-support": "hyprland-qt-support",
+ "hyprlang": [
+ "hyprlang"
+ ],
+ "hyprutils": [
+ "hyprland-qtutils",
+ "hyprlang",
+ "hyprutils"
+ ],
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": [
+ "systems"
+ ]
+ },
+ "locked": {
+ "lastModified": 1739048983,
+ "narHash": "sha256-REhTcXq4qs3B3cCDtLlYDz0GZvmsBSh947Ub6pQWGTQ=",
+ "owner": "hyprwm",
+ "repo": "hyprland-qtutils",
+ "rev": "3504a293c8f8db4127cb0f7cfc1a318ffb4316f8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hyprwm",
+ "repo": "hyprland-qtutils",
+ "type": "github"
+ }
+ },
"hyprlang": {
"inputs": {
"hyprutils": [
@@ -193,11 +215,11 @@
]
},
"locked": {
- "lastModified": 1771866172,
- "narHash": "sha256-fYFoXhQLrm1rD8vSFKQBOEX4OGCuJdLt1amKfHd5GAw=",
+ "lastModified": 1741191527,
+ "narHash": "sha256-kM+11Nch47Xwfgtw2EpRitJuORy4miwoMuRi5tyMBDY=",
"owner": "hyprwm",
"repo": "hyprlang",
- "rev": "0b219224910e7642eb0ed49f0db5ec3d008e3e41",
+ "rev": "72df3861f1197e41b078faa3e38eedd60e00018d",
"type": "github"
},
"original": {
@@ -206,51 +228,6 @@
"type": "github"
}
},
- "hyprtoolkit": {
- "inputs": {
- "aquamarine": [
- "hyprland-guiutils",
- "aquamarine"
- ],
- "hyprgraphics": [
- "hyprland-guiutils",
- "hyprgraphics"
- ],
- "hyprlang": [
- "hyprland-guiutils",
- "hyprlang"
- ],
- "hyprutils": [
- "hyprland-guiutils",
- "hyprutils"
- ],
- "hyprwayland-scanner": [
- "hyprland-guiutils",
- "hyprwayland-scanner"
- ],
- "nixpkgs": [
- "hyprland-guiutils",
- "nixpkgs"
- ],
- "systems": [
- "hyprland-guiutils",
- "systems"
- ]
- },
- "locked": {
- "lastModified": 1764592794,
- "narHash": "sha256-7CcO+wbTJ1L1NBQHierHzheQGPWwkIQug/w+fhTAVuU=",
- "owner": "hyprwm",
- "repo": "hyprtoolkit",
- "rev": "5cfe0743f0e608e1462972303778d8a0859ee63e",
- "type": "github"
- },
- "original": {
- "owner": "hyprwm",
- "repo": "hyprtoolkit",
- "type": "github"
- }
- },
"hyprutils": {
"inputs": {
"nixpkgs": [
@@ -261,11 +238,11 @@
]
},
"locked": {
- "lastModified": 1771271487,
- "narHash": "sha256-41gEiUS0Pyw3L/ge1l8MXn61cK14VAhgWB/JV8s/oNI=",
+ "lastModified": 1741534688,
+ "narHash": "sha256-EV3945SnjOCuRVbGRghsWx/9D89FyshnSO1Q6/TuQ14=",
"owner": "hyprwm",
"repo": "hyprutils",
- "rev": "340a792e3b3d482c4ae5f66d27a9096bdee6d76d",
+ "rev": "dd1f720cbc2dbb3c71167c9598045dd3261d27b3",
"type": "github"
},
"original": {
@@ -284,11 +261,11 @@
]
},
"locked": {
- "lastModified": 1770501770,
- "narHash": "sha256-NWRM6+YxTRv+bT9yvlhhJ2iLae1B1pNH3mAL5wi2rlQ=",
+ "lastModified": 1739870480,
+ "narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
- "rev": "0bd8b6cde9ec27d48aad9e5b4deefb3746909d40",
+ "rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
"type": "github"
},
"original": {
@@ -297,39 +274,13 @@
"type": "github"
}
},
- "hyprwire": {
- "inputs": {
- "hyprutils": [
- "hyprutils"
- ],
- "nixpkgs": [
- "nixpkgs"
- ],
- "systems": [
- "systems"
- ]
- },
- "locked": {
- "lastModified": 1771606233,
- "narHash": "sha256-F3PLUqQ/TwgR70U+UeOqJnihJZ2EuunzojYC4g5xHr0=",
- "owner": "hyprwm",
- "repo": "hyprwire",
- "rev": "06c7f1f8c4194786c8400653c4efc49dc14c0f3a",
- "type": "github"
- },
- "original": {
- "owner": "hyprwm",
- "repo": "hyprwire",
- "type": "github"
- }
- },
"nixpkgs": {
"locked": {
- "lastModified": 1772198003,
- "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=",
+ "lastModified": 1742069588,
+ "narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61",
+ "rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5",
"type": "github"
},
"original": {
@@ -348,11 +299,11 @@
]
},
"locked": {
- "lastModified": 1772024342,
- "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
+ "lastModified": 1742058297,
+ "narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=",
"owner": "cachix",
"repo": "git-hooks.nix",
- "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
+ "rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a",
"type": "github"
},
"original": {
@@ -366,12 +317,11 @@
"aquamarine": "aquamarine",
"hyprcursor": "hyprcursor",
"hyprgraphics": "hyprgraphics",
- "hyprland-guiutils": "hyprland-guiutils",
"hyprland-protocols": "hyprland-protocols",
+ "hyprland-qtutils": "hyprland-qtutils",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils",
"hyprwayland-scanner": "hyprwayland-scanner",
- "hyprwire": "hyprwire",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems",
@@ -415,11 +365,11 @@
]
},
"locked": {
- "lastModified": 1761431178,
- "narHash": "sha256-xzjC1CV3+wpUQKNF+GnadnkeGUCJX+vgaWIZsnz9tzI=",
+ "lastModified": 1741934139,
+ "narHash": "sha256-ZhTcTH9FoeAtbPfWGrhkH7RjLJZ7GeF18nygLAMR+WE=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
- "rev": "4b8801228ff958d028f588f0c2b911dbf32297f9",
+ "rev": "150b0b6f52bb422a1b232a53698606fe0320dde0",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 6d695bfb..801d96ec 100644
--- a/flake.nix
+++ b/flake.nix
@@ -35,15 +35,11 @@
inputs.systems.follows = "systems";
};
- hyprland-guiutils = {
- url = "github:hyprwm/hyprland-guiutils";
+ hyprland-qtutils = {
+ url = "github:hyprwm/hyprland-qtutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
- inputs.aquamarine.follows = "aquamarine";
- inputs.hyprgraphics.follows = "hyprgraphics";
- inputs.hyprutils.follows = "hyprutils";
inputs.hyprlang.follows = "hyprlang";
- inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
};
hyprlang = {
@@ -65,13 +61,6 @@
inputs.systems.follows = "systems";
};
- hyprwire = {
- url = "github:hyprwm/hyprwire";
- inputs.nixpkgs.follows = "nixpkgs";
- inputs.systems.follows = "systems";
- inputs.hyprutils.follows = "hyprutils";
- };
-
xdph = {
url = "github:hyprwm/xdg-desktop-portal-hyprland";
inputs.nixpkgs.follows = "nixpkgs";
@@ -88,122 +77,91 @@
};
};
- outputs =
- inputs@{
- self,
- nixpkgs,
- systems,
- ...
- }:
- let
- inherit (nixpkgs) lib;
- eachSystem = lib.genAttrs (import systems);
- pkgsFor = eachSystem (
- system:
- import nixpkgs {
- localSystem = system;
- overlays = with self.overlays; [
- hyprland-packages
- hyprland-extras
- ];
- }
- );
- pkgsCrossFor = eachSystem (
- system: crossSystem:
- import nixpkgs {
- localSystem = system;
- inherit crossSystem;
- overlays = with self.overlays; [
- hyprland-packages
- hyprland-extras
- ];
- }
- );
- pkgsDebugFor = eachSystem (
- system:
- import nixpkgs {
- localSystem = system;
- overlays = with self.overlays; [
- hyprland-debug
- ];
- }
- );
- pkgsDebugCrossFor = eachSystem (
- system: crossSystem:
- import nixpkgs {
- localSystem = system;
- inherit crossSystem;
- overlays = with self.overlays; [
- hyprland-debug
- ];
- }
- );
- in
- {
- overlays = import ./nix/overlays.nix { inherit self lib inputs; };
+ outputs = inputs @ {
+ self,
+ nixpkgs,
+ systems,
+ ...
+ }: let
+ inherit (nixpkgs) lib;
+ eachSystem = lib.genAttrs (import systems);
+ pkgsFor = eachSystem (system:
+ import nixpkgs {
+ localSystem = system;
+ overlays = with self.overlays; [
+ hyprland-packages
+ hyprland-extras
+ ];
+ });
+ pkgsCrossFor = eachSystem (system: crossSystem:
+ import nixpkgs {
+ localSystem = system;
+ inherit crossSystem;
+ overlays = with self.overlays; [
+ hyprland-packages
+ hyprland-extras
+ ];
+ });
+ in {
+ overlays = import ./nix/overlays.nix {inherit self lib inputs;};
- checks = eachSystem (
- system:
- (lib.filterAttrs (
- n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)
- ) self.packages.${system})
- // {
- inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
- pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
- src = ./.;
- hooks = {
- hyprland-treewide-formatter = {
- enable = true;
- entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
- pass_filenames = false;
- excludes = [ "subprojects" ];
- always_run = true;
- };
+ checks = eachSystem (system:
+ (lib.filterAttrs
+ (n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n))
+ self.packages.${system})
+ // {
+ inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
+ pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
+ src = ./.;
+ hooks = {
+ hyprland-treewide-formatter = {
+ enable = true;
+ entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
+ pass_filenames = false;
+ excludes = ["subprojects"];
+ always_run = true;
};
};
- }
- // (import ./nix/tests inputs pkgsFor.${system})
- );
-
- packages = eachSystem (system: {
- default = self.packages.${system}.hyprland;
- inherit (pkgsFor.${system})
- # hyprland-packages
- hyprland
- hyprland-unwrapped
- hyprland-with-tests
- # hyprland-extras
- xdg-desktop-portal-hyprland
- ;
- inherit (pkgsDebugFor.${system}) hyprland-debug;
- hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
- hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug;
+ };
});
- devShells = eachSystem (system: {
- default =
- pkgsFor.${system}.mkShell.override
- {
- inherit (self.packages.${system}.default) stdenv;
- }
- {
- name = "hyprland-shell";
- hardeningDisable = [ "fortify" ];
- inputsFrom = [ pkgsFor.${system}.hyprland ];
- packages = [ pkgsFor.${system}.clang-tools ];
- inherit (self.checks.${system}.pre-commit-check) shellHook;
- };
- });
+ packages = eachSystem (system: {
+ default = self.packages.${system}.hyprland;
+ inherit
+ (pkgsFor.${system})
+ # hyprland-packages
+ hyprland
+ hyprland-debug
+ hyprland-legacy-renderer
+ hyprland-unwrapped
+ # hyprland-extras
+ xdg-desktop-portal-hyprland
+ ;
+ hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
+ });
- formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix { });
+ devShells = eachSystem (system: {
+ default =
+ pkgsFor.${system}.mkShell.override {
+ inherit (self.packages.${system}.default) stdenv;
+ } {
+ name = "hyprland-shell";
+ hardeningDisable = ["fortify"];
+ inputsFrom = [pkgsFor.${system}.hyprland];
+ packages = [pkgsFor.${system}.clang-tools];
+ inherit (self.checks.${system}.pre-commit-check) shellHook;
+ };
+ });
- nixosModules.default = import ./nix/module.nix inputs;
- homeManagerModules.default = import ./nix/hm-module.nix self;
+ formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
- # Hydra build jobs
- # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix
- # or similar. Remember to filter large or incompatible attributes here. More eval jobs can
- # be added by merging, e.g., self.packages // self.devShells.
- hydraJobs = self.packages;
- };
+ nixosModules.default = import ./nix/module.nix inputs;
+ homeManagerModules.default = import ./nix/hm-module.nix self;
+
+ # Hydra build jobs
+ # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' intead of a release.nix
+ # or similar. Remember to filter large or incompatible attributes here. More eval jobs can
+ # be added by merging, e.g., self.packages // self.devShells.
+ hydraJobs = self.packages;
+ };
}
diff --git a/hyprctl/CMakeLists.txt b/hyprctl/CMakeLists.txt
index 7071ede9..db5ef615 100644
--- a/hyprctl/CMakeLists.txt
+++ b/hyprctl/CMakeLists.txt
@@ -5,32 +5,11 @@ project(
DESCRIPTION "Control utility for Hyprland"
)
-pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 hyprwire re2)
+pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2)
-file(GLOB_RECURSE HYPRCTL_SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "hw-protocols/*.cpp" "include/*.hpp")
-
-add_executable(hyprctl ${HYPRCTL_SRCFILES})
+add_executable(hyprctl "main.cpp")
target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
-target_include_directories(hyprctl PRIVATE "hw-protocols")
-
-# Hyprwire
-
-function(hyprprotocol protoPath protoName)
- set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
- add_custom_command(
- OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.hpp
- ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-spec.hpp
- COMMAND hyprwire-scanner --client ${path}/${protoName}.xml
- ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
- target_sources(hyprctl PRIVATE hw-protocols/${protoName}-client.cpp
- hw-protocols/${protoName}-client.hpp
- hw-protocols/${protoName}-spec.hpp)
-endfunction()
-
-hyprprotocol(hw-protocols hyprpaper_core)
# binary
install(TARGETS hyprctl)
diff --git a/hyprctl/src/Strings.hpp b/hyprctl/Strings.hpp
similarity index 92%
rename from hyprctl/src/Strings.hpp
rename to hyprctl/Strings.hpp
index 549d84bb..c70ca737 100644
--- a/hyprctl/src/Strings.hpp
+++ b/hyprctl/Strings.hpp
@@ -49,7 +49,6 @@ commands:
the same format as in colors in config. Will reset
when Hyprland's config is reloaded
setprop ... → Sets a window property
- getprop ... → Gets a window property
splash → Get the current splash
switchxkblayout ... → Sets the xkb layout index for a keyboard
systeminfo → Get system info
@@ -74,8 +73,11 @@ flags:
const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper
requests:
- wallpaper → Issue a wallpaper to call a config wallpaper dynamically.
- Arguments are [mon],[path],[fit_mode]. Fit mode is optional.
+ listactive → Lists all active images
+ listloaded → Lists all loaded images
+ preload → Preloads image
+ unload → Unloads image. Pass 'all' as path to unload all images
+ wallpaper → Issue a wallpaper to call a config wallpaper dynamically
flags:
See 'hyprctl --help')#";
@@ -144,7 +146,7 @@ regex:
Regular expression by which a window will be searched
property:
- See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list
+ See https://wiki.hyprland.org/Configuring/Using-hyprctl/#setprop for list
of properties
value:
@@ -157,18 +159,6 @@ lock:
flags:
See 'hyprctl --help')#";
-const std::string_view GETPROP_HELP = R"#(usage: hyprctl [flags] getprop
-
-regex:
- Regular expression by which a window will be searched
-
-property:
- See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list
- of properties
-
-flags:
- See 'hyprctl --help')#";
-
const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout
device:
diff --git a/hyprctl/hw-protocols/hyprpaper_core.xml b/hyprctl/hw-protocols/hyprpaper_core.xml
deleted file mode 100644
index 3d26a102..00000000
--- a/hyprctl/hw-protocols/hyprpaper_core.xml
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
- BSD 3-Clause License
-
- Copyright (c) 2025, Hypr Development
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- 3. Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/hyprctl/hyprctl.bash b/hyprctl/hyprctl.bash
index ba541653..e26e623e 100644
--- a/hyprctl/hyprctl.bash
+++ b/hyprctl/hyprctl.bash
@@ -23,7 +23,7 @@ _hyprctl () {
local words cword
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
- declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize sendkeystate)
+ declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize)
declare -A literal_transitions
literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)"
literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)"
diff --git a/hyprctl/hyprctl.fish b/hyprctl/hyprctl.fish
index 6ad32041..11309416 100644
--- a/hyprctl/hyprctl.fish
+++ b/hyprctl/hyprctl.fish
@@ -29,7 +29,7 @@ function _hyprctl
set COMP_CWORD (count $COMP_WORDS)
end
- set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate"
+ set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize"
set descriptions
set descriptions[1] "Resize the active window"
@@ -48,7 +48,7 @@ function _hyprctl
set descriptions[22] "Focus the urgent window or the last window"
set descriptions[23] "Get the list of defined workspace rules"
set descriptions[24] "Move the active workspace to a monitor"
- set descriptions[25] "Move window doesn't switch to the workspace"
+ set descriptions[25] "Move window doesnt switch to the workspace"
set descriptions[26] "Interact with hyprpaper if present"
set descriptions[29] "Swap the active window with the next or previous in a group"
set descriptions[30] "Move the cursor to the corner of the active window"
diff --git a/hyprctl/hyprctl.usage b/hyprctl/hyprctl.usage
index e13fc9d5..b2c55682 100644
--- a/hyprctl/hyprctl.usage
+++ b/hyprctl/hyprctl.usage
@@ -1,4 +1,4 @@
-# This is a file fed to complgen to generate bash/fish/zsh completions
+# This is a file feeded to complgen to generate bash/fish/zsh completions
# Repo: https://github.com/adaszko/complgen
# Generate completion scripts: "complgen aot --bash-script hyprctl.bash --fish-script hyprctl.fish --zsh-script hyprctl.zsh ./hyprctl.usage"
@@ -106,12 +106,11 @@ hyprctl []...
| (execr) "Execute a raw shell command"
| (pass) "Pass the key to a specified window"
| (sendshortcut) "On shortcut X sends shortcut Y to a specified window"
- | (sendkeystate) "Send a key with specific state (down/repeat/up) to a specified window (window must keep focus for events to continue)"
| (killactive) "Close the active window"
| (closewindow) "Close a specified window"
| (workspace) "Change the workspace"
| (movetoworkspace) "Move the focused window to a workspace"
- | (movetoworkspacesilent) "Move window doesn't switch to the workspace"
+ | (movetoworkspacesilent) "Move window doesnt switch to the workspace"
| (togglefloating) "Toggle the current window's floating state"
| (setfloating) "Set the current window's floating state to true"
| (settiled) "Set the current window's floating state to false"
diff --git a/hyprctl/hyprctl.zsh b/hyprctl/hyprctl.zsh
index e0c48ab0..9a858100 100644
--- a/hyprctl/hyprctl.zsh
+++ b/hyprctl/hyprctl.zsh
@@ -17,7 +17,7 @@ _hyprctl_cmd_0 () {
}
_hyprctl () {
- local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate")
+ local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize")
local -A descriptions
descriptions[1]="Resize the active window"
@@ -36,7 +36,7 @@ _hyprctl () {
descriptions[22]="Focus the urgent window or the last window"
descriptions[23]="Get the list of defined workspace rules"
descriptions[24]="Move the active workspace to a monitor"
- descriptions[25]="Move window doesn't switch to the workspace"
+ descriptions[25]="Move window doesnt switch to the workspace"
descriptions[26]="Interact with hyprpaper if present"
descriptions[29]="Swap the active window with the next or previous in a group"
descriptions[30]="Move the cursor to the corner of the active window"
diff --git a/hyprctl/src/main.cpp b/hyprctl/main.cpp
similarity index 74%
rename from hyprctl/src/main.cpp
rename to hyprctl/main.cpp
index 0a33f3ed..25a4eef3 100644
--- a/hyprctl/src/main.cpp
+++ b/hyprctl/main.cpp
@@ -14,9 +14,6 @@
#include
#include
#include
-#include
-#include
-#include
#include
#include
@@ -26,12 +23,11 @@
#include
#include
#include
-#include
using namespace Hyprutils::String;
-using namespace Hyprutils::Memory;
#include "Strings.hpp"
-#include "hyprpaper/Hyprpaper.hpp"
+
+#define PAD
std::string instanceSignature;
bool quiet = false;
@@ -41,9 +37,10 @@ struct SInstanceData {
uint64_t time;
uint64_t pid;
std::string wlSocket;
+ bool valid = true;
};
-void log(const std::string_view str) {
+void log(const std::string& str) {
if (quiet)
return;
@@ -67,74 +64,47 @@ std::string getRuntimeDir() {
return std::string{XDG} + "/hypr";
}
-static std::optional toUInt64(const std::string_view str) {
- uint64_t value = 0;
- const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
- if (ec != std::errc() || ptr != str.data() + str.size())
- return std::nullopt;
- return value;
-}
-
-static std::optional parseInstance(const std::filesystem::directory_entry& entry) {
- if (!entry.is_directory())
- return std::nullopt;
-
- const auto lockPath = entry.path() / "hyprland.lock";
- std::ifstream ifs(lockPath);
- if (!ifs.is_open())
- return std::nullopt;
-
- SInstanceData data;
- data.id = entry.path().filename().string();
-
- const auto first = std::string_view{data.id}.find_first_of('_');
- const auto last = std::string_view{data.id}.find_last_of('_');
- if (first == std::string_view::npos || last == std::string_view::npos || last <= first)
- return std::nullopt;
-
- auto time = toUInt64(std::string_view{data.id}.substr(first + 1, last - first - 1));
- if (!time)
- return std::nullopt;
- data.time = *time;
-
- std::string line;
- if (!std::getline(ifs, line))
- return std::nullopt;
-
- auto pid = toUInt64(std::string_view{line});
- if (!pid)
- return std::nullopt;
- data.pid = *pid;
-
- if (!std::getline(ifs, data.wlSocket))
- return std::nullopt;
-
- if (std::getline(ifs, line) && !line.empty())
- return std::nullopt; // more lines than expected
-
- return data;
-}
-
std::vector instances() {
std::vector result;
- std::error_code ec;
- const auto runtimeDir = getRuntimeDir();
- if (!std::filesystem::exists(runtimeDir, ec) || ec)
- return result;
+ try {
+ if (!std::filesystem::exists(getRuntimeDir()))
+ return {};
+ } catch (std::exception& e) { return {}; }
- std::filesystem::directory_iterator it(runtimeDir, std::filesystem::directory_options::skip_permission_denied, ec);
- if (ec)
- return result;
+ for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
+ if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
+ continue;
- for (const auto& el : it) {
- if (auto instance = parseInstance(el))
- result.emplace_back(std::move(*instance));
+ // read lock
+ SInstanceData* data = &result.emplace_back();
+ data->id = el.path().filename().string();
+
+ try {
+ data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1, data->id.find_last_of('_') - (data->id.find_first_of('_') + 1)));
+ } catch (std::exception& e) { continue; }
+
+ // read file
+ std::ifstream ifs(el.path().string() + "/hyprland.lock");
+
+ int i = 0;
+ for (std::string line; std::getline(ifs, line); ++i) {
+ if (i == 0) {
+ try {
+ data->pid = std::stoull(line);
+ } catch (std::exception& e) { continue; }
+ } else if (i == 1) {
+ data->wlSocket = line;
+ } else
+ break;
+ }
+
+ ifs.close();
}
- std::erase_if(result, [](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
+ std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
- std::ranges::sort(result, {}, &SInstanceData::time);
+ std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; });
return result;
}
@@ -176,7 +146,7 @@ int rollingRead(const int socket) {
return 0;
}
-int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
+int request(std::string arg, int minArgs = 0, bool needRoll = false) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
if (SERVERSOCKET < 0) {
@@ -202,19 +172,21 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
return 3;
}
- sockaddr_un serverAddress = {0};
- serverAddress.sun_family = AF_UNIX;
+ const std::string USERID = std::to_string(getUID());
+
+ sockaddr_un serverAddress = {0};
+ serverAddress.sun_family = AF_UNIX;
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.socket.sock";
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
- if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
+ if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
log("Couldn't connect to " + socketPath + ". (4)");
return 4;
}
- auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size());
+ auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
if (sizeWritten < 0) {
log("Couldn't write (5)");
@@ -228,23 +200,23 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
constexpr size_t BUFFER_SIZE = 8192;
char buffer[BUFFER_SIZE] = {0};
- // read all data until server closes the connection
- // this handles partial writes on the server side under high load
- while (true) {
- sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
+ sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
+ if (sizeWritten < 0) {
+ if (errno == EWOULDBLOCK)
+ log("Hyprland IPC didn't respond in time\n");
+ log("Couldn't read (6)");
+ return 6;
+ }
+
+ reply += std::string(buffer, sizeWritten);
+
+ while (sizeWritten == BUFFER_SIZE) {
+ sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
if (sizeWritten < 0) {
- if (errno == EWOULDBLOCK)
- log("Hyprland IPC didn't respond in time\n");
log("Couldn't read (6)");
return 6;
}
-
- if (sizeWritten == 0) {
- // server closed connection, we're done
- break;
- }
-
reply += std::string(buffer, sizeWritten);
}
@@ -255,7 +227,7 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
return 0;
}
-int requestIPC(std::string_view filename, std::string_view arg) {
+int requestIPC(std::string filename, std::string arg) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
if (SERVERSOCKET < 0) {
@@ -271,11 +243,13 @@ int requestIPC(std::string_view filename, std::string_view arg) {
sockaddr_un serverAddress = {0};
serverAddress.sun_family = AF_UNIX;
- std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename;
+ const std::string USERID = std::to_string(getUID());
+
+ std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename;
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
- if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
+ if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
log("Couldn't connect to " + socketPath + ". (3)");
return 3;
}
@@ -283,7 +257,7 @@ int requestIPC(std::string_view filename, std::string_view arg) {
arg = arg.substr(arg.find_first_of('/') + 1); // strip flags
arg = arg.substr(arg.find_first_of(' ') + 1); // strip "hyprpaper"
- auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size());
+ auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
if (sizeWritten < 0) {
log("Couldn't write (4)");
@@ -306,12 +280,16 @@ int requestIPC(std::string_view filename, std::string_view arg) {
return 0;
}
-int requestHyprsunset(std::string_view arg) {
+int requestHyprpaper(std::string arg) {
+ return requestIPC(".hyprpaper.sock", arg);
+}
+
+int requestHyprsunset(std::string arg) {
return requestIPC(".hyprsunset.sock", arg);
}
-void batchRequest(std::string_view arg, bool json) {
- std::string commands(arg.substr(arg.find_first_of(' ') + 1));
+void batchRequest(std::string arg, bool json) {
+ std::string commands = arg.substr(arg.find_first_of(' ') + 1);
if (json) {
RE2::GlobalReplace(&commands, ";\\s*", ";j/");
@@ -382,7 +360,7 @@ int main(int argc, char** argv) {
parseArgs = false;
continue;
}
- if (parseArgs && (ARGS[i][0] == '-') && !(isNumber(ARGS[i], true) || isNumber(ARGS[i].substr(0, ARGS[i].length() - 1), true)) /* For stuff like -2 or -2, */) {
+ if (parseArgs && (ARGS[i][0] == '-') && !isNumber(ARGS[i], true) /* For stuff like -2 */) {
// parse
if (ARGS[i] == "-j" && !fullArgs.contains("j")) {
fullArgs += "j";
@@ -424,8 +402,6 @@ int main(int argc, char** argv) {
std::println("{}", PLUGIN_HELP);
} else if (cmd == "setprop") {
std::println("{}", SETPROP_HELP);
- } else if (cmd == "getprop") {
- std::println("{}", GETPROP_HELP);
} else if (cmd == "switchxkblayout") {
std::println("{}", SWITCHXKBLAYOUT_HELP);
} else {
@@ -476,7 +452,7 @@ int main(int argc, char** argv) {
const auto INSTANCES = instances();
- if (INSTANCENO < 0 || sc(INSTANCENO) >= INSTANCES.size()) {
+ if (INSTANCENO < 0 || static_cast(INSTANCENO) >= INSTANCES.size()) {
log("no such instance\n");
return 1;
}
@@ -497,12 +473,9 @@ int main(int argc, char** argv) {
if (fullRequest.contains("/--batch"))
batchRequest(fullRequest, json);
- else if (fullRequest.contains("/hyprpaper")) {
- auto result = Hyprpaper::makeHyprpaperRequest(fullRequest);
- if (!result)
- log(std::format("error: {}", result.error()));
- exitStatus = !result;
- } else if (fullRequest.contains("/hyprsunset"))
+ else if (fullRequest.contains("/hyprpaper"))
+ exitStatus = requestHyprpaper(fullRequest);
+ else if (fullRequest.contains("/hyprsunset"))
exitStatus = requestHyprsunset(fullRequest);
else if (fullRequest.contains("/switchxkblayout"))
exitStatus = request(fullRequest, 2);
diff --git a/hyprctl/meson.build b/hyprctl/meson.build
new file mode 100644
index 00000000..d6769b84
--- /dev/null
+++ b/hyprctl/meson.build
@@ -0,0 +1,27 @@
+executable(
+ 'hyprctl',
+ 'main.cpp',
+ dependencies: [
+ dependency('hyprutils', version: '>= 0.1.1'),
+ dependency('re2', required: true)
+ ],
+ install: true,
+)
+
+install_data(
+ 'hyprctl.bash',
+ install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
+ install_tag: 'runtime',
+ rename: 'hyprctl',
+)
+install_data(
+ 'hyprctl.fish',
+ install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
+ install_tag: 'runtime',
+)
+install_data(
+ 'hyprctl.zsh',
+ install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
+ install_tag: 'runtime',
+ rename: '_hyprctl',
+)
diff --git a/hyprctl/src/helpers/Memory.hpp b/hyprctl/src/helpers/Memory.hpp
deleted file mode 100644
index 1d3a9e07..00000000
--- a/hyprctl/src/helpers/Memory.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-using namespace Hyprutils::Memory;
-
-#define SP CSharedPointer
-#define WP CWeakPointer
-#define UP CUniquePointer
diff --git a/hyprctl/src/hyprpaper/Hyprpaper.cpp b/hyprctl/src/hyprpaper/Hyprpaper.cpp
deleted file mode 100644
index 93f4182a..00000000
--- a/hyprctl/src/hyprpaper/Hyprpaper.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-#include "Hyprpaper.hpp"
-#include "../helpers/Memory.hpp"
-
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-using namespace Hyprutils::String;
-
-using namespace std::string_literals;
-
-constexpr const char* SOCKET_NAME = ".hyprpaper.sock";
-static SP g_coreImpl;
-
-constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 2;
-
-//
-static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) {
- if (sv == "contain")
- return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_CONTAIN;
- if (sv == "fit" || sv == "stretch")
- return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_STRETCH;
- if (sv == "tile")
- return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_TILE;
- return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_COVER;
-}
-
-static std::expected resolvePath(const std::string_view& sv) {
- std::error_code ec;
- auto can = std::filesystem::canonical(sv, ec);
-
- if (ec)
- return std::unexpected(std::format("invalid path: {}", ec.message()));
-
- return can;
-}
-
-static std::expected getFullPath(const std::string_view& sv) {
- if (sv.empty())
- return std::unexpected("empty path");
-
- if (sv[0] == '~') {
- static auto HOME = getenv("HOME");
- if (!HOME || HOME[0] == '\0')
- return std::unexpected("home path but no $HOME");
-
- return resolvePath(std::string{HOME} + "/"s + std::string{sv.substr(1)});
- }
-
- return resolvePath(sv);
-}
-
-static std::expected doWallpaper(const std::string_view& RHS) {
- CVarList2 args(std::string{RHS}, 0, ',');
-
- const std::string MONITOR = std::string{args[0]};
- const auto& PATH_RAW = args[1];
- const auto& FIT = args[2];
-
- if (PATH_RAW.empty())
- return std::unexpected("not enough args");
-
- const auto RTDIR = getenv("XDG_RUNTIME_DIR");
-
- if (!RTDIR || RTDIR[0] == '\0')
- return std::unexpected("can't send: no XDG_RUNTIME_DIR");
-
- const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
-
- if (!HIS || HIS[0] == '\0')
- return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)");
-
- const auto PATH = getFullPath(PATH_RAW);
-
- if (!PATH)
- return std::unexpected(std::format("bad path: {}", PATH_RAW));
-
- auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME;
-
- auto socket = Hyprwire::IClientSocket::open(socketPath);
-
- if (!socket)
- return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)");
-
- g_coreImpl = makeShared(PROTOCOL_VERSION_SUPPORTED);
-
- socket->addImplementation(g_coreImpl);
-
- if (!socket->waitForHandshake())
- return std::unexpected("can't send: wire handshake failed");
-
- auto spec = socket->getSpec(g_coreImpl->protocol()->specName());
-
- if (!spec)
- return std::unexpected("can't send: hyprpaper doesn't have the spec?!");
-
- auto manager = makeShared(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
-
- if (!manager)
- return std::unexpected("wire error: couldn't create manager");
-
- auto wallpaper = makeShared(manager->sendGetWallpaperObject());
-
- if (!wallpaper)
- return std::unexpected("wire error: couldn't create wallpaper object");
-
- bool canExit = false;
- std::optional err;
-
- wallpaper->setFailed([&canExit, &err](uint32_t code) {
- canExit = true;
- switch (code) {
- case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_PATH: err = std::format("failed to set wallpaper: Invalid path", code); break;
- case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_MONITOR: err = std::format("failed to set wallpaper: Invalid monitor", code); break;
- default: err = std::format("failed to set wallpaper: unknown error, code {}", code); break;
- }
- });
- wallpaper->setSuccess([&canExit]() { canExit = true; });
-
- wallpaper->sendPath(PATH->c_str());
- wallpaper->sendMonitorName(MONITOR.c_str());
- if (!FIT.empty())
- wallpaper->sendFitMode(fitFromString(FIT));
-
- wallpaper->sendApply();
-
- while (!canExit) {
- socket->dispatchEvents(true);
- }
-
- if (err)
- return std::unexpected(*err);
-
- return {};
-}
-
-static std::expected doListActive() {
- const auto RTDIR = getenv("XDG_RUNTIME_DIR");
-
- if (!RTDIR || RTDIR[0] == '\0')
- return std::unexpected("can't send: no XDG_RUNTIME_DIR");
-
- const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
-
- if (!HIS || HIS[0] == '\0')
- return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)");
-
- auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME;
-
- auto socket = Hyprwire::IClientSocket::open(socketPath);
-
- if (!socket)
- return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)");
-
- g_coreImpl = makeShared(PROTOCOL_VERSION_SUPPORTED);
-
- socket->addImplementation(g_coreImpl);
-
- if (!socket->waitForHandshake())
- return std::unexpected("can't send: wire handshake failed");
-
- auto spec = socket->getSpec(g_coreImpl->protocol()->specName());
-
- if (!spec)
- return std::unexpected("can't send: hyprpaper doesn't have the spec?!");
-
- if (spec->specVer() < 2)
- return std::unexpected("can't send: hyprpaper protocol version too low (hyprpaper too old)");
-
- auto manager = makeShared(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
-
- if (!manager)
- return std::unexpected("wire error: couldn't create manager");
-
- auto status = makeShared(manager->sendGetStatusObject());
-
- status->setActiveWallpaper([](const char* mon, const char* wp) { std::println("{}: {}", mon, wp); });
-
- socket->roundtrip();
-
- return {};
-}
-
-std::expected Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) {
- if (!rq.contains(' '))
- return std::unexpected("Invalid request");
-
- if (!rq.starts_with("/hyprpaper "))
- return std::unexpected("Invalid request");
-
- std::string_view LHS, RHS;
- auto spacePos = rq.find(' ', 12);
- LHS = rq.substr(11, spacePos - 11);
- RHS = rq.substr(spacePos + 1);
-
- if (LHS == "wallpaper")
- return doWallpaper(RHS);
- else if (LHS == "listactive")
- return doListActive();
- else
- return std::unexpected("invalid hyprpaper request");
-
- return {};
-}
diff --git a/hyprctl/src/hyprpaper/Hyprpaper.hpp b/hyprctl/src/hyprpaper/Hyprpaper.hpp
deleted file mode 100644
index 167b0a8d..00000000
--- a/hyprctl/src/hyprpaper/Hyprpaper.hpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace Hyprpaper {
- std::expected makeHyprpaperRequest(const std::string_view& rq);
-};
\ No newline at end of file
diff --git a/hyprland.pc.in b/hyprland.pc.in
index bf5764f3..81a0ef11 100644
--- a/hyprland.pc.in
+++ b/hyprland.pc.in
@@ -4,5 +4,4 @@ Name: Hyprland
URL: https://github.com/hyprwm/Hyprland
Description: Hyprland header files
Version: @HYPRLAND_VERSION@
-Requires: aquamarine >= @AQUAMARINE_MINIMUM_VERSION@, hyprcursor >= @HYPRCURSOR_MINIMUM_VERSION@, hyprgraphics >= @HYPRGRAPHICS_MINIMUM_VERSION@, hyprlang >= @HYPRLANG_MINIMUM_VERSION@, hyprutils >= @HYPRUTILS_MINIMUM_VERSION@, libdrm, egl, cairo, xkbcommon >= @XKBCOMMON_MINIMUM_VERSION@, libinput >= @LIBINPUT_MINIMUM_VERSION@, wayland-server >= @WAYLAND_SERVER_MINIMUM_VERSION@@PKGCONFIG_XWAYLAND_DEPENDENCIES@
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland
diff --git a/hyprpm/CMakeLists.txt b/hyprpm/CMakeLists.txt
index 7d5b8eda..d744ac16 100644
--- a/hyprpm/CMakeLists.txt
+++ b/hyprpm/CMakeLists.txt
@@ -9,11 +9,11 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
set(CMAKE_CXX_STANDARD 23)
-pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
+pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.2.4)
-find_package(glaze 6.0.1 QUIET)
+find_package(glaze QUIET)
if (NOT glaze_FOUND)
- set(GLAZE_VERSION v6.0.1)
+ set(GLAZE_VERSION v4.2.3)
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
include(FetchContent)
FetchContent_Declare(
@@ -21,7 +21,6 @@ if (NOT glaze_FOUND)
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
GIT_TAG ${GLAZE_VERSION}
GIT_SHALLOW TRUE
- EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(glaze)
endif()
diff --git a/hyprpm/hyprpm.fish b/hyprpm/hyprpm.fish
index 56e301eb..82561bd8 100644
--- a/hyprpm/hyprpm.fish
+++ b/hyprpm/hyprpm.fish
@@ -29,8 +29,8 @@ function _hyprpm
set descriptions[6] "Show help menu"
set descriptions[7] "Check and update all plugins if needed"
set descriptions[8] "Install a new plugin repository from git"
- set descriptions[9] "Enable too much logging"
- set descriptions[10] "Enable too much logging"
+ set descriptions[9] "Enable too much loggin"
+ set descriptions[10] "Enable too much loggin"
set descriptions[11] "Force an operation ignoring checks (e.g. update -f)"
set descriptions[12] "Disable shallow cloning of Hyprland sources"
set descriptions[13] "Remove a plugin repository"
diff --git a/hyprpm/hyprpm.usage b/hyprpm/hyprpm.usage
index 0d7c07fb..c38bdd62 100644
--- a/hyprpm/hyprpm.usage
+++ b/hyprpm/hyprpm.usage
@@ -3,7 +3,7 @@ hyprpm []...
::= (--notify | -n) "Send a hyprland notification for important events (e.g. load fail)"
| (--help | -h) "Show help menu"
- | (--verbose | -v) "Enable too much logging"
+ | (--verbose | -v) "Enable too much loggin"
| (--force | -f) "Force an operation ignoring checks (e.g. update -f)"
| (--no-shallow | -s) "Disable shallow cloning of Hyprland sources"
;
diff --git a/hyprpm/hyprpm.zsh b/hyprpm/hyprpm.zsh
index 3946a695..859c5313 100644
--- a/hyprpm/hyprpm.zsh
+++ b/hyprpm/hyprpm.zsh
@@ -19,8 +19,8 @@ _hyprpm () {
descriptions[6]="Show help menu"
descriptions[7]="Check and update all plugins if needed"
descriptions[8]="Install a new plugin repository from git"
- descriptions[9]="Enable too much logging"
- descriptions[10]="Enable too much logging"
+ descriptions[9]="Enable too much loggin"
+ descriptions[10]="Enable too much loggin"
descriptions[11]="Force an operation ignoring checks (e.g. update -f)"
descriptions[12]="Disable shallow cloning of Hyprland sources"
descriptions[13]="Remove a plugin repository"
diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp
index 64f3cfa0..55f148a4 100644
--- a/hyprpm/src/core/DataState.cpp
+++ b/hyprpm/src/core/DataState.cpp
@@ -1,44 +1,22 @@
#include "DataState.hpp"
-#include
#include
#include
-#include
#include
#include "PluginManager.hpp"
-#include "../helpers/Die.hpp"
-#include "../helpers/Sys.hpp"
-#include "../helpers/StringUtils.hpp"
-
-static std::string getTempRoot() {
- static auto ENV = getenv("XDG_RUNTIME_DIR");
- if (!ENV) {
- std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n";
- exit(1);
- }
-
- const auto STR = ENV + std::string{"/hyprpm/"};
-
- if (!std::filesystem::exists(STR))
- mkdir(STR.c_str(), S_IRWXU);
-
- return STR;
-}
-
-// write the state to a file
-static bool writeState(const std::string& str, const std::string& to) {
- // create temp file in a safe temp root
- std::ofstream of(getTempRoot() + ".temp-state", std::ios::trunc);
- if (!of.good())
- return false;
-
- of << str;
- of.close();
-
- return NSys::root::install(getTempRoot() + ".temp-state", to, "644");
-}
std::filesystem::path DataState::getDataStatePath() {
- return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername);
+ const auto HOME = getenv("HOME");
+ if (!HOME) {
+ std::println(stderr, "DataState: no $HOME");
+ throw std::runtime_error("no $HOME");
+ return "";
+ }
+
+ const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME");
+
+ if (XDG_DATA_HOME)
+ return std::filesystem::path{XDG_DATA_HOME} / "hyprpm";
+ return std::filesystem::path{HOME} / ".local/share/hyprpm";
}
std::string DataState::getHeadersPath() {
@@ -63,37 +41,25 @@ std::vector DataState::getPluginStates() {
}
void DataState::ensureStateStoreExists() {
- std::error_code ec;
- if (!std::filesystem::exists(getHeadersPath(), ec) || ec) {
- std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now..."));
- if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) {
- if (!NSys::root::createDirectory("/var/cache/hyprpm", "755"))
- Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
- }
- if (!std::filesystem::exists(getDataStatePath(), ec) || ec) {
- if (!NSys::root::createDirectory(getDataStatePath().string(), "755"))
- Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
- }
- if (!NSys::root::createDirectory(getHeadersPath(), "755"))
- Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
- }
+ const auto PATH = getDataStatePath();
+
+ if (!std::filesystem::exists(PATH))
+ std::filesystem::create_directories(PATH);
+
+ if (!std::filesystem::exists(getHeadersPath()))
+ std::filesystem::create_directories(getHeadersPath());
}
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
ensureStateStoreExists();
- const auto PATH = getDataStatePath() / repo.name;
+ const auto PATH = getDataStatePath() / repo.name;
- std::error_code ec;
- if (!std::filesystem::exists(PATH, ec) || ec) {
- if (!NSys::root::createDirectory(PATH.string(), "755"))
- Debug::die("addNewPluginRepo: failed to create cache dir");
- }
+ std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"repository", toml::table{
{"name", repo.name},
- {"author", repo.author},
{"hash", repo.hash},
{"url", repo.url},
{"rev", repo.rev}
@@ -102,11 +68,9 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
for (auto const& p : repo.plugins) {
const auto filename = p.name + ".so";
- // copy .so to the good place and chmod 755
- if (std::filesystem::exists(p.filename)) {
- if (!NSys::root::install(p.filename, (PATH / filename).string(), "0755"))
- Debug::die("addNewPluginRepo: failed to install so file");
- }
+ // copy .so to the good place
+ if (std::filesystem::exists(p.filename))
+ std::filesystem::copy_file(p.filename, PATH / filename);
DATA.emplace(p.name, toml::table{
{"filename", filename},
@@ -116,39 +80,40 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
}
// clang-format on
- std::stringstream ss;
- ss << DATA;
-
- if (!writeState(ss.str(), (PATH / "state.toml").string()))
- Debug::die("{}", failureString("Failed to write plugin state"));
+ std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
+ ofs << DATA;
+ ofs.close();
}
-bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) {
+bool DataState::pluginRepoExists(const std::string& urlOrName) {
ensureStateStoreExists();
- for (const auto& stateFile : getPluginStates()) {
- const auto STATE = toml::parse_file(stateFile.c_str());
- const auto NAME = STATE["repository"]["name"].value_or("");
- const auto AUTHOR = STATE["repository"]["author"].value_or("");
- const auto URL = STATE["repository"]["url"].value_or("");
+ const auto PATH = getDataStatePath();
- if (identifier.matches(URL, NAME, AUTHOR))
+ for (const auto& stateFile : getPluginStates()) {
+ const auto STATE = toml::parse_file(stateFile.c_str());
+ const auto NAME = STATE["repository"]["name"].value_or("");
+ const auto URL = STATE["repository"]["url"].value_or("");
+
+ if (URL == urlOrName || NAME == urlOrName)
return true;
}
return false;
}
-void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
+void DataState::removePluginRepo(const std::string& urlOrName) {
ensureStateStoreExists();
- for (const auto& stateFile : getPluginStates()) {
- const auto STATE = toml::parse_file(stateFile.c_str());
- const auto NAME = STATE["repository"]["name"].value_or("");
- const auto AUTHOR = STATE["repository"]["author"].value_or("");
- const auto URL = STATE["repository"]["url"].value_or("");
+ const auto PATH = getDataStatePath();
+
+ for (const auto& stateFile : getPluginStates()) {
+ const auto STATE = toml::parse_file(stateFile.c_str());
+ const auto NAME = STATE["repository"]["name"].value_or("");
+ const auto URL = STATE["repository"]["url"].value_or("");
+
+ if (URL == urlOrName || NAME == urlOrName) {
- if (identifier.matches(URL, NAME, AUTHOR)) {
// unload the plugins!!
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
if (!file.path().string().ends_with(".so"))
@@ -157,14 +122,7 @@ void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
}
- const auto PATH = stateFile.parent_path().string();
-
- if (!PATH.starts_with("/var/cache/hyprpm") || PATH.contains('\''))
- return; // WTF?
-
- // scary!
- if (!NSys::root::removeRecursive(PATH))
- Debug::die("removePluginRepo: failed to remove dir");
+ std::filesystem::remove_all(stateFile.parent_path());
return;
}
}
@@ -173,43 +131,36 @@ void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
void DataState::updateGlobalState(const SGlobalState& state) {
ensureStateStoreExists();
- const auto PATH = getDataStatePath();
+ const auto PATH = getDataStatePath();
- std::error_code ec;
- if (!std::filesystem::exists(PATH, ec) || ec) {
- if (!NSys::root::createDirectory(PATH.string(), "755"))
- Debug::die("updateGlobalState: failed to create dir");
- }
+ std::filesystem::create_directories(PATH);
// clang-format off
auto DATA = toml::table{
{"state", toml::table{
- {"hash", state.headersAbiCompiled},
+ {"hash", state.headersHashCompiled},
{"dont_warn_install", state.dontWarnInstall}
}}
};
// clang-format on
- std::stringstream ss;
- ss << DATA;
-
- if (!writeState(ss.str(), (PATH / "state.toml").string()))
- Debug::die("{}", failureString("Failed to write plugin state"));
+ std::ofstream ofs(PATH / "state.toml", std::ios::trunc);
+ ofs << DATA;
+ ofs.close();
}
SGlobalState DataState::getGlobalState() {
ensureStateStoreExists();
- const auto stateFile = getDataStatePath() / "state.toml";
+ const auto stateFile = getDataStatePath() / "state.toml";
- std::error_code ec;
- if (!std::filesystem::exists(stateFile, ec) || ec)
+ if (!std::filesystem::exists(stateFile))
return SGlobalState{};
auto DATA = toml::parse_file(stateFile.c_str());
SGlobalState state;
- state.headersAbiCompiled = DATA["state"]["hash"].value_or("");
- state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
+ state.headersHashCompiled = DATA["state"]["hash"].value_or("");
+ state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
return state;
}
@@ -217,22 +168,22 @@ SGlobalState DataState::getGlobalState() {
std::vector DataState::getAllRepositories() {
ensureStateStoreExists();
+ const auto PATH = getDataStatePath();
+
std::vector repos;
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
- const auto NAME = STATE["repository"]["name"].value_or("");
- const auto AUTHOR = STATE["repository"]["author"].value_or("");
- const auto URL = STATE["repository"]["url"].value_or("");
- const auto REV = STATE["repository"]["rev"].value_or("");
- const auto HASH = STATE["repository"]["hash"].value_or("");
+ const auto NAME = STATE["repository"]["name"].value_or("");
+ const auto URL = STATE["repository"]["url"].value_or("");
+ const auto REV = STATE["repository"]["rev"].value_or("");
+ const auto HASH = STATE["repository"]["hash"].value_or("");
SPluginRepository repo;
- repo.hash = HASH;
- repo.name = NAME;
- repo.author = AUTHOR;
- repo.url = URL;
- repo.rev = REV;
+ repo.hash = HASH;
+ repo.name = NAME;
+ repo.url = URL;
+ repo.rev = REV;
for (const auto& [key, val] : STATE) {
if (key == "repository")
@@ -251,26 +202,19 @@ std::vector DataState::getAllRepositories() {
return repos;
}
-bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) {
+bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
ensureStateStoreExists();
+ const auto PATH = getDataStatePath();
+
for (const auto& stateFile : getPluginStates()) {
const auto STATE = toml::parse_file(stateFile.c_str());
for (const auto& [key, val] : STATE) {
if (key == "repository")
continue;
- switch (identifier.type) {
- case IDENTIFIER_NAME:
- if (key.str() != identifier.name)
- continue;
- break;
- case IDENTIFIER_AUTHOR_NAME:
- if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name)
- continue;
- break;
- default: return false;
- }
+ if (key.str() != name)
+ continue;
const auto FAILED = STATE[key]["failed"].value_or(false);
@@ -280,11 +224,9 @@ bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool en
auto modifiedState = STATE;
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
- std::stringstream ss;
- ss << modifiedState;
-
- if (!writeState(ss.str(), stateFile.string()))
- Debug::die("{}", failureString("Failed to write plugin state"));
+ std::ofstream state(stateFile, std::ios::trunc);
+ state << modifiedState;
+ state.close();
return true;
}
@@ -292,18 +234,3 @@ bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool en
return false;
}
-
-void DataState::purgeAllCache() {
- std::error_code ec;
- if (!std::filesystem::exists(getDataStatePath()) && !ec) {
- std::println("{}", infoString("Nothing to do"));
- return;
- }
-
- const auto PATH = getDataStatePath().string();
- if (PATH.contains('\''))
- return;
- // scary!
- if (!NSys::root::removeRecursive(PATH))
- Debug::die("Failed to run a superuser cmd");
-}
diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp
index d9872b90..ebb550ba 100644
--- a/hyprpm/src/core/DataState.hpp
+++ b/hyprpm/src/core/DataState.hpp
@@ -5,8 +5,8 @@
#include "Plugin.hpp"
struct SGlobalState {
- std::string headersAbiCompiled = "";
- bool dontWarnInstall = false;
+ std::string headersHashCompiled = "";
+ bool dontWarnInstall = false;
};
namespace DataState {
@@ -15,11 +15,10 @@ namespace DataState {
std::vector getPluginStates();
void ensureStateStoreExists();
void addNewPluginRepo(const SPluginRepository& repo);
- void removePluginRepo(const SPluginRepoIdentifier identifier);
- bool pluginRepoExists(const SPluginRepoIdentifier identifier);
+ void removePluginRepo(const std::string& urlOrName);
+ bool pluginRepoExists(const std::string& urlOrName);
void updateGlobalState(const SGlobalState& state);
- void purgeAllCache();
SGlobalState getGlobalState();
- bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled);
+ bool setPluginEnabled(const std::string& name, bool enabled);
std::vector getAllRepositories();
-};
+};
\ No newline at end of file
diff --git a/hyprpm/src/core/HyprlandSocket.cpp b/hyprpm/src/core/HyprlandSocket.cpp
deleted file mode 100644
index 47142e54..00000000
--- a/hyprpm/src/core/HyprlandSocket.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "HyprlandSocket.hpp"
-#include
-#include
-#include "../helpers/StringUtils.hpp"
-#include
-#include
-#include
-#include
-#include
-
-using namespace Hyprutils::Memory;
-
-static int getUID() {
- const auto UID = getuid();
- const auto PWUID = getpwuid(UID);
- return PWUID ? PWUID->pw_uid : UID;
-}
-
-static std::string getRuntimeDir() {
- const auto XDG = getenv("XDG_RUNTIME_DIR");
-
- if (!XDG) {
- const std::string USERID = std::to_string(getUID());
- return "/run/user/" + USERID + "/hypr";
- }
-
- return std::string{XDG} + "/hypr";
-}
-
-std::string NHyprlandSocket::send(const std::string& cmd) {
- const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
-
- if (SERVERSOCKET < 0) {
- std::println("{}", failureString("Couldn't open a socket (1)"));
- return "";
- }
-
- const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
-
- if (!HIS) {
- std::println("{}", failureString("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)"));
- return "";
- }
-
- sockaddr_un serverAddress = {0};
- serverAddress.sun_family = AF_UNIX;
-
- std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock";
-
- strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
-
- if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
- std::println("{}", failureString("Couldn't connect to " + socketPath + ". (4)"));
- return "";
- }
-
- auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
-
- if (sizeWritten < 0) {
- std::println("{}", failureString("Couldn't write (5)"));
- return "";
- }
-
- std::string reply = "";
- constexpr size_t BUFFER_SIZE = 8192;
- char buffer[BUFFER_SIZE] = {0};
-
- sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
-
- if (sizeWritten < 0) {
- std::println("{}", failureString("Couldn't read (6)"));
- return "";
- }
-
- reply += std::string(buffer, sizeWritten);
-
- while (sizeWritten == BUFFER_SIZE) {
- sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
- if (sizeWritten < 0) {
- std::println("{}", failureString("Couldn't read (7)"));
- return "";
- }
- reply += std::string(buffer, sizeWritten);
- }
-
- close(SERVERSOCKET);
-
- return reply;
-}
diff --git a/hyprpm/src/core/HyprlandSocket.hpp b/hyprpm/src/core/HyprlandSocket.hpp
deleted file mode 100644
index e33be5ca..00000000
--- a/hyprpm/src/core/HyprlandSocket.hpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include
-
-namespace NHyprlandSocket {
- std::string send(const std::string& cmd);
-};
\ No newline at end of file
diff --git a/hyprpm/src/core/Manifest.cpp b/hyprpm/src/core/Manifest.cpp
index 90f6fbff..754d9d69 100644
--- a/hyprpm/src/core/Manifest.cpp
+++ b/hyprpm/src/core/Manifest.cpp
@@ -1,12 +1,6 @@
#include "Manifest.hpp"
#include
-#include
-
-// Alphanumerics and -_ allowed for plugin names. No magic names.
-// [A-Za-z0-9\-_]*
-static bool validManifestName(const std::string_view& n) {
- return std::ranges::all_of(n, [](const char& c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == '=' || (c >= '0' && c <= '9'); });
-}
+#include
CManifest::CManifest(const eManifestType type, const std::string& path) {
auto manifest = toml::parse_file(path);
@@ -17,17 +11,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
continue;
CManifest::SManifestPlugin plugin;
-
- if (!validManifestName(key.str())) {
- m_good = false;
- return;
- }
-
plugin.name = key;
- m_plugins.push_back(plugin);
+ m_vPlugins.push_back(plugin);
}
- for (auto& plugin : m_plugins) {
+ for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.version = manifest[plugin.name]["version"].value_or("?");
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
@@ -49,21 +37,21 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
- m_good = false;
+ m_bGood = false;
return;
}
}
} else if (type == MANIFEST_HYPRPM) {
- m_repository.name = manifest["repository"]["name"].value_or("");
- auto authors = manifest["repository"]["authors"].as_array();
+ m_sRepository.name = manifest["repository"]["name"].value_or("");
+ auto authors = manifest["repository"]["authors"].as_array();
if (authors) {
for (auto&& a : *authors) {
- m_repository.authors.push_back(a.as_string()->value_or("?"));
+ m_sRepository.authors.push_back(a.as_string()->value_or("?"));
}
} else {
auto author = manifest["repository"]["author"].value_or("");
if (!std::string{author}.empty())
- m_repository.authors.push_back(author);
+ m_sRepository.authors.push_back(author);
}
auto pins = manifest["repository"]["commit_pins"].as_array();
@@ -71,7 +59,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
for (auto&& pin : *pins) {
auto pinArr = pin.as_array();
if (pinArr && pinArr->get(1))
- m_repository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
+ m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
}
}
@@ -80,17 +68,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
continue;
CManifest::SManifestPlugin plugin;
-
- if (!validManifestName(key.str())) {
- m_good = false;
- return;
- }
-
plugin.name = key;
- m_plugins.push_back(plugin);
+ m_vPlugins.push_back(plugin);
}
- for (auto& plugin : m_plugins) {
+ for (auto& plugin : m_vPlugins) {
plugin.description = manifest[plugin.name]["description"].value_or("?");
plugin.output = manifest[plugin.name]["output"].value_or("?");
plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0);
@@ -112,12 +94,12 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
}
if (plugin.output.empty() || plugin.buildSteps.empty()) {
- m_good = false;
+ m_bGood = false;
return;
}
}
} else {
// ???
- m_good = false;
+ m_bGood = false;
}
}
\ No newline at end of file
diff --git a/hyprpm/src/core/Manifest.hpp b/hyprpm/src/core/Manifest.hpp
index 41a49350..19f967eb 100644
--- a/hyprpm/src/core/Manifest.hpp
+++ b/hyprpm/src/core/Manifest.hpp
@@ -27,8 +27,8 @@ class CManifest {
std::string name;
std::vector authors;
std::vector> commitPins;
- } m_repository;
+ } m_sRepository;
- std::vector m_plugins;
- bool m_good = true;
+ std::vector m_vPlugins;
+ bool m_bGood = true;
};
\ No newline at end of file
diff --git a/hyprpm/src/core/Plugin.cpp b/hyprpm/src/core/Plugin.cpp
deleted file mode 100644
index 3aaa8592..00000000
--- a/hyprpm/src/core/Plugin.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "Plugin.hpp"
-
-SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) {
- return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url};
-}
-
-SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) {
- return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name};
-}
-
-SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) {
- return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
-}
-
-SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) {
- if (string.find(':') != std::string::npos) {
- return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string};
- } else {
- auto slashPos = string.find('/');
- if (slashPos != std::string::npos) {
- std::string author = string.substr(0, slashPos);
- std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1);
- return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
- } else {
- return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string};
- }
- }
-}
-
-std::string SPluginRepoIdentifier::toString() const {
- switch (type) {
- case IDENTIFIER_NAME: return name;
- case IDENTIFIER_AUTHOR_NAME: return author + '/' + name;
- case IDENTIFIER_URL: return url;
- }
-
- return "";
-}
-
-bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const {
- switch (type) {
- case IDENTIFIER_URL: return this->url == url;
- case IDENTIFIER_NAME: return this->name == name;
- case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name;
- }
-
- return false;
-}
diff --git a/hyprpm/src/core/Plugin.hpp b/hyprpm/src/core/Plugin.hpp
index a8c74084..e66031c9 100644
--- a/hyprpm/src/core/Plugin.hpp
+++ b/hyprpm/src/core/Plugin.hpp
@@ -14,27 +14,6 @@ struct SPluginRepository {
std::string url;
std::string rev;
std::string name;
- std::string author;
std::vector plugins;
std::string hash;
-};
-
-enum ePluginRepoIdentifierType {
- IDENTIFIER_URL,
- IDENTIFIER_NAME,
- IDENTIFIER_AUTHOR_NAME
-};
-
-struct SPluginRepoIdentifier {
- ePluginRepoIdentifierType type;
- std::string url = "";
- std::string name = "";
- std::string author = "";
-
- static SPluginRepoIdentifier fromString(const std::string& string);
- static SPluginRepoIdentifier fromUrl(const std::string& Url);
- static SPluginRepoIdentifier fromName(const std::string& name);
- static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name);
- std::string toString() const;
- bool matches(const std::string& url, const std::string& name, const std::string& author) const;
-};
+};
\ No newline at end of file
diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp
index 6621a49f..3343cbe0 100644
--- a/hyprpm/src/core/PluginManager.cpp
+++ b/hyprpm/src/core/PluginManager.cpp
@@ -4,14 +4,10 @@
#include "../progress/CProgressBar.hpp"
#include "Manifest.hpp"
#include "DataState.hpp"
-#include "HyprlandSocket.hpp"
-#include "../helpers/Sys.hpp"
-#include "../helpers/Die.hpp"
#include
#include
#include
-#include
#include
#include
#include
@@ -27,10 +23,8 @@
#include
#include
-#include
using namespace Hyprutils::String;
using namespace Hyprutils::OS;
-using namespace Hyprutils::Memory;
static std::string execAndGet(std::string cmd) {
cmd += " 2>&1";
@@ -55,13 +49,6 @@ static std::string getTempRoot() {
return STR;
}
-CPluginManager::CPluginManager() {
- if (NSys::isSuperuser())
- Debug::die("Don't run hyprpm as a superuser.");
-
- m_szUsername = getpwuid(NSys::getUID())->pw_name;
-}
-
SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
static bool onceRunning = false;
static bool onceInstalled = false;
@@ -79,33 +66,40 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
else
onceInstalled = true;
- const auto HLVERCALL = running ? NHyprlandSocket::send("j/version") : execAndGet("Hyprland --version-json");
+ const auto HLVERCALL = running ? execAndGet("hyprctl version") : execAndGet("Hyprland --version");
+ if (m_bVerbose)
+ std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
- auto jsonQuery = glz::read_json(HLVERCALL);
-
- if (!jsonQuery) {
- std::println("{}", failureString("failed to get the current hyprland version. Are you running hyprland?"));
+ if (!HLVERCALL.contains("Tag:")) {
+ std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
return SHyprlandVersion{};
}
- auto hlbranch = (*jsonQuery)["branch"].get_string();
- auto hlcommit = (*jsonQuery)["commit"].get_string();
- auto abiHash = (*jsonQuery)["abiHash"].get_string();
- auto hldate = (*jsonQuery)["commit_date"].get_string();
- auto hlcommits = (*jsonQuery)["commits"].get_string();
+ std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
+ hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
- auto flags = (*jsonQuery)["flags"].get_array();
- bool isNix = std::ranges::any_of(flags, [](const auto& f) { return f.is_string() && f.get_string() == std::string_view{"nix"}; });
+ std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
+ hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
- size_t commits = 0;
+ std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
+ hldate = hldate.substr(0, hldate.find('\n'));
+
+ std::string hlcommits;
+
+ if (HLVERCALL.contains("commits:")) {
+ hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
+ hlcommits = hlcommits.substr(0, hlcommits.find(' '));
+ }
+
+ int commits = 0;
try {
- commits = std::stoull(hlcommits);
+ commits = std::stoi(hlcommits);
} catch (...) { ; }
if (m_bVerbose)
- std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}, nix: {}", hlcommit, hlbranch, hldate, commits, isNix));
+ std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
- auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, abiHash, commits, isNix};
+ auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
if (running)
verRunning = ver;
@@ -131,24 +125,15 @@ bool CPluginManager::createSafeDirectory(const std::string& path) {
return true;
}
-bool CPluginManager::validArg(const std::string& s) {
- return !s.contains("'") && !s.ends_with("\\") && !s.starts_with("\\");
-}
-
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
const auto HLVER = getHyprlandVersion();
- if (!validArg(url) || !validArg(rev)) {
- std::println(stderr, "\n{}", failureString("url or rev invalid"));
- return false;
- }
-
if (!hasDeps()) {
- std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
+ std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
return false;
}
- if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) {
+ if (DataState::pluginRepoExists(url)) {
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
return false;
}
@@ -156,18 +141,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
auto GLOBALSTATE = DataState::getGlobalState();
if (!GLOBALSTATE.dontWarnInstall) {
std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET);
- std::println("plugins, especially not official, have no guarantee of stability, availability or security.\n"
+ std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n"
"Run them at your own risk.\n"
"This message will not appear again.");
GLOBALSTATE.dontWarnInstall = true;
DataState::updateGlobalState(GLOBALSTATE);
}
- if (GLOBALSTATE.headersAbiCompiled.empty()) {
- std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first."));
- return false;
- }
-
std::cout << Colors::GREEN << "✔" << Colors::RESET << Colors::RED << " adding a new plugin repository " << Colors::RESET << "from " << url << "\n " << Colors::RED
<< "MAKE SURE" << Colors::RESET << " that you trust the authors. " << Colors::RED << "DO NOT" << Colors::RESET
<< " install random plugins without verifying the code and author.\n "
@@ -207,7 +187,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(infoString("Cloning {}", url));
- std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), url, USERNAME));
+ std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
@@ -245,14 +225,14 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
return false;
}
- if (!pManifest->m_good) {
+ if (!pManifest->m_bGood) {
std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
return false;
}
progress.m_iSteps = 2;
- progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_plugins.size()) + " plugins:"));
- for (auto const& pl : pManifest->m_plugins) {
+ progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:"));
+ for (auto const& pl : pManifest->m_vPlugins) {
std::string message = "→ " + pl.name + " by ";
for (auto const& a : pl.authors) {
message += a + ", ";
@@ -265,12 +245,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(message);
}
- if (rev.empty() && !pManifest->m_repository.commitPins.empty()) {
+ if (!pManifest->m_sRepository.commitPins.empty()) {
// check commit pins
- progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
+ progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
- for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
+ for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
@@ -293,7 +273,6 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
if (HEADERSSTATUS != HEADERS_OK) {
std::println("\n{}", headerError(HEADERSSTATUS));
- std::println("\n{}", infoString("if the problem persists, try running hyprpm purge-cache."));
return false;
}
@@ -302,7 +281,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.m_szCurrentMessage = "Building plugin(s)";
progress.print();
- for (auto& p : pManifest->m_plugins) {
+ for (auto& p : pManifest->m_vPlugins) {
std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) {
@@ -314,14 +293,8 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto const& bs : p.buildSteps) {
- const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER);
-
- if (!CMD_RAW) {
- progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error()));
- break;
- }
-
- out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n";
+ const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
+ out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (m_bVerbose)
@@ -351,14 +324,11 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
if (repohash.length() > 0)
repohash.pop_back();
- auto lastSlash = url.find_last_of('/');
- auto secondLastSlash = url.find_last_of('/', lastSlash - 1);
- repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name;
- repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1);
- repo.url = url;
- repo.rev = rev;
- repo.hash = repohash;
- for (auto const& p : pManifest->m_plugins) {
+ repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name;
+ repo.url = url;
+ repo.rev = rev;
+ repo.hash = repohash;
+ for (auto const& p : pManifest->m_vPlugins) {
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
}
DataState::addNewPluginRepo(repo);
@@ -377,13 +347,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
return true;
}
-bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
- if (!DataState::pluginRepoExists(identifier)) {
+bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
+ if (!DataState::pluginRepoExists(urlOrName)) {
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
return false;
}
- std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n "
+ std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
<< "Are you sure? [Y/n] ";
std::fflush(stdout);
std::string input;
@@ -394,7 +364,7 @@ bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
return false;
}
- DataState::removePluginRepo(identifier);
+ DataState::removePluginRepo(urlOrName);
return true;
}
@@ -406,7 +376,7 @@ eHeadersErrors CPluginManager::headersValid() {
return HEADERS_MISSING;
// find headers commit
- const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}\" pkgconf --cflags --keep-system-cflags hyprland", getPkgConfigPath());
+ const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
auto headers = execAndGet(cmd);
if (!headers.contains("-I/"))
@@ -455,22 +425,17 @@ eHeadersErrors CPluginManager::headersValid() {
if (hash != HLVER.hash)
return HEADERS_MISMATCHED;
- // check ABI hash too
- const auto GLOBALSTATE = DataState::getGlobalState();
-
- if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash)
- return HEADERS_ABI_MISMATCH;
-
return HEADERS_OK;
}
bool CPluginManager::updateHeaders(bool force) {
+
DataState::ensureStateStoreExists();
const auto HLVER = getHyprlandVersion(false);
if (!hasDeps()) {
- std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
+ std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config"));
return false;
}
@@ -512,11 +477,11 @@ bool CPluginManager::updateHeaders(bool force) {
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
std::string ret =
- execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
+ execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
if (!std::filesystem::exists(WORKINGDIR)) {
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
- ret = execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}", getTempRoot(), HL_URL, USERNAME));
+ ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME));
}
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
@@ -559,17 +524,8 @@ bool CPluginManager::updateHeaders(bool force) {
if (m_bVerbose)
progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
- const auto CONFIGURE_CMD =
- nixDevelopIfNeeded(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build", WORKINGDIR,
- DataState::getHeadersPath()),
- HLVER);
-
- if (!CONFIGURE_CMD) {
- std::println(stderr, "\n{}", failureString("Could not configure hyprland: {}", CONFIGURE_CMD.error()));
- return false;
- }
-
- ret = execAndGet(*CONFIGURE_CMD);
+ ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR,
+ DataState::getHeadersPath()));
if (m_bVerbose)
progress.printMessageAbove(verboseString("cmake returned: {}", ret));
@@ -592,21 +548,13 @@ bool CPluginManager::updateHeaders(bool force) {
progress.m_szCurrentMessage = "Installing sources";
progress.print();
- std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile", DataState::getHeadersPath(), WORKINGDIR);
+ const std::string& cmd =
+ std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR);
if (m_bVerbose)
- progress.printMessageAbove(verboseString("prepare install will run: {}", cmd));
+ progress.printMessageAbove(verboseString("installation will run: {}", cmd));
ret = execAndGet(cmd);
- cmd = std::format("make -C '{}' installheaders && chmod -R 644 '{}' && find '{}' -type d -exec chmod a+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(),
- DataState::getHeadersPath());
-
- if (m_bVerbose)
- progress.printMessageAbove(verboseString("install will run as sudo: {}", cmd));
-
- // WORKINGDIR and headersPath should not contain anything unsafe. Usernames can't contain cmd exec parts.
- ret = NSys::root::runAsSuperuserUnsafe(cmd);
-
if (m_bVerbose)
std::println("{}", verboseString("installer returned: {}", ret));
@@ -614,21 +562,15 @@ bool CPluginManager::updateHeaders(bool force) {
std::filesystem::remove_all(WORKINGDIR);
auto HEADERSVALID = headersValid();
-
- if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED || HEADERSVALID == HEADERS_ABI_MISMATCH) {
+ if (HEADERSVALID == HEADERS_OK) {
progress.printMessageAbove(successString("installed headers"));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
progress.print();
- auto GLOBALSTATE = DataState::getGlobalState();
- GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
- DataState::updateGlobalState(GLOBALSTATE);
-
std::print("\n");
} else {
- progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", sc(HEADERSVALID), headerErrorShort(HEADERSVALID)));
- progress.printMessageAbove(infoString("if the problem persists, try running hyprpm purge-cache."));
+ progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Failed";
progress.print();
@@ -657,7 +599,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
const auto HLVER = getHyprlandVersion(false);
CProgressBar progress;
- progress.m_iMaxSteps = (REPOS.size() * 2) + 2;
+ progress.m_iMaxSteps = REPOS.size() * 2 + 2;
progress.m_iSteps = 0;
progress.m_szCurrentMessage = "Updating repositories";
progress.print();
@@ -678,7 +620,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(infoString("Cloning {}", repo.url));
- std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), repo.url, USERNAME));
+ std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME));
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
@@ -688,7 +630,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (!repo.rev.empty()) {
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
- std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules \'" + repo.rev + "\'");
+ std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
if (ret.compare(0, 6, "fatal:") == 0) {
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
@@ -735,17 +677,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
continue;
}
- if (!pManifest->m_good) {
- std::println(stderr, "\n{}", failureString("The provided plugin repository has a bad manifest"));
+ if (!pManifest->m_bGood) {
+ std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest"));
continue;
}
- if (repo.rev.empty() && !pManifest->m_repository.commitPins.empty()) {
+ if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) {
// check commit pins unless a revision is specified
- progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size()));
+ progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size()));
- for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) {
+ for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) {
if (hl != HLVER.hash)
continue;
@@ -755,7 +697,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
}
}
- for (auto& p : pManifest->m_plugins) {
+ for (auto& p : pManifest->m_vPlugins) {
std::string out;
if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) {
@@ -767,14 +709,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.printMessageAbove(infoString("Building {}", p.name));
for (auto const& bs : p.buildSteps) {
- const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER);
-
- if (!CMD_RAW) {
- progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error()));
- break;
- }
-
- out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n";
+ const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
+ out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
}
if (m_bVerbose)
@@ -803,11 +739,11 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
if (repohash.length() > 0)
repohash.pop_back();
newrepo.hash = repohash;
- for (auto const& p : pManifest->m_plugins) {
- const auto OLDPLUGINIT = std::ranges::find_if(repo.plugins, [&](const auto& other) { return other.name == p.name; });
- newrepo.plugins.emplace_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
+ for (auto const& p : pManifest->m_vPlugins) {
+ const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
+ newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
}
- DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name));
+ DataState::removePluginRepo(newrepo.name);
DataState::addNewPluginRepo(newrepo);
std::filesystem::remove_all(m_szWorkingPluginDirectory);
@@ -819,8 +755,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
progress.m_szCurrentMessage = "Updating global state...";
progress.print();
- auto GLOBALSTATE = DataState::getGlobalState();
- GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
+ auto GLOBALSTATE = DataState::getGlobalState();
+ GLOBALSTATE.headersHashCompiled = HLVER.hash;
DataState::updateGlobalState(GLOBALSTATE);
progress.m_iSteps++;
@@ -832,23 +768,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
return true;
}
-bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) {
- bool ret = false;
-
- switch (identifier.type) {
- case IDENTIFIER_NAME:
- case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break;
- default: return false;
- }
+bool CPluginManager::enablePlugin(const std::string& name) {
+ bool ret = DataState::setPluginEnabled(name, true);
if (ret)
- std::println("{}", successString("Enabled {}", identifier.name));
+ std::println("{}", successString("Enabled {}", name));
return ret;
}
-bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) {
- bool ret = DataState::setPluginEnabled(identifier, false);
+bool CPluginManager::disablePlugin(const std::string& name) {
+ bool ret = DataState::setPluginEnabled(name, false);
if (ret)
- std::println("{}", successString("Disabled {}", identifier.name));
+ std::println("{}", successString("Disabled {}", name));
return ret;
}
@@ -866,9 +796,9 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
}
const auto HYPRPMPATH = DataState::getDataStatePath();
- const auto json = glz::read_json(NHyprlandSocket::send("j/plugins list"));
+ const auto json = glz::read_json(execAndGet("hyprctl plugins list -j"));
if (!json) {
- std::println(stderr, "PluginManager: couldn't parse plugin list output");
+ std::println(stderr, "PluginManager: couldn't parse hyprctl output");
return LOADSTATE_FAIL;
}
@@ -931,7 +861,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
if (!p.enabled)
continue;
- if (!forceReload && std::ranges::find_if(loadedPlugins, [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
+ if (!forceReload && std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
continue;
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) {
@@ -951,16 +881,15 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
auto state = DataState::getGlobalState();
auto HLVER = getHyprlandVersion(true);
- if (state.headersAbiCompiled != HLVER.abiHash) {
- if (load)
- std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersAbiCompiled));
+ if (state.headersHashCompiled != HLVER.hash) {
+ std::println("{}", infoString("Running Hyprland version differs from plugin state, please restart Hyprland."));
return false;
}
if (load)
- NHyprlandSocket::send("/plugin load " + path);
+ execAndGet("hyprctl plugin load " + path);
else
- NHyprlandSocket::send("/plugin unload " + path);
+ execAndGet("hyprctl plugin unload " + path);
return true;
}
@@ -969,7 +898,7 @@ void CPluginManager::listAllPlugins() {
const auto REPOS = DataState::getAllRepositories();
for (auto const& r : REPOS) {
- std::println("{}", infoString("Repository {} (by {}):", r.name, r.author));
+ std::println("{}", infoString("Repository {}:", r.name));
for (auto const& p : r.plugins) {
std::println(" │ Plugin {}", p.name);
@@ -985,7 +914,7 @@ void CPluginManager::listAllPlugins() {
}
void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message) {
- NHyprlandSocket::send("/notify " + std::to_string(icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
+ execAndGet("hyprctl notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message);
}
std::string CPluginManager::headerError(const eHeadersErrors err) {
@@ -994,7 +923,6 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
- case HEADERS_ABI_MISMATCH: return failureString("ABI is mismatched. Please run hyprpm update to fix that.\n");
case HEADERS_DUPLICATED: {
return failureString("Headers duplicated!!! This is a very bad sign.\n"
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"
@@ -1019,107 +947,11 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
}
bool CPluginManager::hasDeps() {
- if (!m_bNoNix && getHyprlandVersion().isNix)
- return true; // dep check not needed if we are on nix
-
- bool hasAllDeps = true;
- std::vector deps = {"cpio", "cmake", "pkg-config", "g++", "gcc", "git"};
-
+ std::vector deps = {"meson", "cpio", "cmake", "pkg-config"};
for (auto const& d : deps) {
- if (!execAndGet("command -v " + d).contains("/")) {
- std::println(stderr, "{}", failureString("Missing dependency: {}", d));
- hasAllDeps = false;
- }
+ if (!execAndGet("command -v " + d).contains("/"))
+ return false;
}
- return hasAllDeps;
-}
-
-const std::string& CPluginManager::getPkgConfigPath() {
- static const auto str = std::format("{}/share/pkgconfig:$PKG_CONFIG_PATH", DataState::getHeadersPath());
- return str;
-}
-
-static std::expected getNixDevelopFromPath(const std::string& argv0) {
- std::string fullStorePath;
-
- if (argv0.starts_with("/")) {
- // we can use this directly
- fullStorePath = argv0;
- } else {
- // use hyprpm, find in path
- auto exe = NSys::findInPath("hyprpm");
- if (!exe)
- return std::unexpected("hyprpm not found in PATH");
-
- fullStorePath = *exe;
- }
-
- if (fullStorePath.empty() || !fullStorePath.ends_with("/bin/hyprpm"))
- return std::unexpected("couldn't get a real path for hyprpm (1)");
-
- // canonicalize to get the real nix-store path
- std::error_code ec;
- fullStorePath = std::filesystem::canonical(fullStorePath, ec);
-
- if (ec || fullStorePath.empty() || !fullStorePath.starts_with("/nix"))
- return std::unexpected("couldn't get a real path for hyprpm");
-
- fullStorePath = fullStorePath.substr(0, fullStorePath.length() - std::string_view{"/bin/hyprpm"}.length());
-
- auto deriver = trim(execAndGet(std::format("echo \"$(nix-store --query --deriver '{}')\"", fullStorePath)));
-
- if (deriver.starts_with("unknown"))
- return std::unexpected("couldn't nix deriver");
-
- return deriver;
-}
-
-static std::expected getNixDevelopFromProfile() {
- const auto NIX_PROFILE_STR = execAndGet("nix profile list --json");
-
- auto rawJson = glz::read_json(NIX_PROFILE_STR);
-
- if (!rawJson)
- return std::unexpected("failed to parse nix profile list --json");
-
- auto& json = *rawJson;
-
- if (!json.contains("elements") || !json["elements"].is_object())
- return std::unexpected("nix profile list --json returned a wonky json");
-
- if (!json["elements"].contains("hyprland") && !json["elements"].contains("Hyprland"))
- return std::unexpected("nix profile list --json doesn't contain Hyprland (did you uninstall?)");
-
- auto& hyprlandJson = json["elements"].contains("hyprland") ? json["elements"]["hyprland"] : json["elements"]["Hyprland"];
-
- if (!hyprlandJson.contains("originalUrl"))
- return std::unexpected("nix profile list --json's hyprland doesn't contain originalUrl?");
-
- return hyprlandJson["originalUrl"].get_string();
-}
-
-std::expected CPluginManager::nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver) {
- if (m_bNoNix || !ver.isNix)
- return cmd;
-
- // Escape single quotes
- std::string newCmd = cmd;
- replaceInString(newCmd, "'", "\\'");
-
- auto NIX_DEVELOP = getNixDevelopFromPath(m_szArgv0);
-
- if (NIX_DEVELOP)
- return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd);
- else if (m_bVerbose)
- std::println("{}", verboseString("Failed nix from path: {}", NIX_DEVELOP.error()));
-
- NIX_DEVELOP = getNixDevelopFromProfile();
-
- if (NIX_DEVELOP)
- return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd);
- else if (m_bVerbose)
- std::println("{}", verboseString("Failed nix from profile: {}", NIX_DEVELOP.error()));
-
- return std::unexpected("hyprland is nix, but hyprpm failed to obtain a nix develop shell for build cmd");
+ return true;
}
diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp
index 25878f54..b159b921 100644
--- a/hyprpm/src/core/PluginManager.hpp
+++ b/hyprpm/src/core/PluginManager.hpp
@@ -1,11 +1,8 @@
#pragma once
-#include
#include
-#include
#include
-#include
-#include "Plugin.hpp"
+#include
enum eHeadersErrors {
HEADERS_OK = 0,
@@ -13,7 +10,6 @@ enum eHeadersErrors {
HEADERS_MISSING,
HEADERS_CORRUPTED,
HEADERS_MISMATCHED,
- HEADERS_ABI_MISMATCH,
HEADERS_DUPLICATED
};
@@ -39,17 +35,13 @@ struct SHyprlandVersion {
std::string branch;
std::string hash;
std::string date;
- std::string abiHash;
int commits = 0;
- bool isNix = false;
};
class CPluginManager {
public:
- CPluginManager();
-
bool addNewPluginRepo(const std::string& url, const std::string& rev);
- bool removePluginRepo(const SPluginRepoIdentifier identifier);
+ bool removePluginRepo(const std::string& urlOrName);
eHeadersErrors headersValid();
bool updateHeaders(bool force = false);
@@ -57,8 +49,8 @@ class CPluginManager {
void listAllPlugins();
- bool enablePlugin(const SPluginRepoIdentifier identifier);
- bool disablePlugin(const SPluginRepoIdentifier identifier);
+ bool enablePlugin(const std::string& name);
+ bool disablePlugin(const std::string& name);
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
bool loadUnloadPlugin(const std::string& path, bool load);
@@ -66,26 +58,20 @@ class CPluginManager {
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
- const std::string& getPkgConfigPath();
-
bool hasDeps();
bool m_bVerbose = false;
bool m_bNoShallow = false;
- bool m_bNoNix = false;
- std::string m_szCustomHlUrl, m_szUsername, m_szArgv0;
+ std::string m_szCustomHlUrl;
// will delete recursively if exists!!
bool createSafeDirectory(const std::string& path);
private:
- std::string headerError(const eHeadersErrors err);
- std::string headerErrorShort(const eHeadersErrors err);
- bool validArg(const std::string& s);
+ std::string headerError(const eHeadersErrors err);
+ std::string headerErrorShort(const eHeadersErrors err);
- std::expected nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver);
-
- std::string m_szWorkingPluginDirectory;
+ std::string m_szWorkingPluginDirectory;
};
inline std::unique_ptr g_pPluginManager;
diff --git a/hyprpm/src/helpers/Die.hpp b/hyprpm/src/helpers/Die.hpp
deleted file mode 100644
index 0c00bcf3..00000000
--- a/hyprpm/src/helpers/Die.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include
-#include
-
-// NOLINTNEXTLINE
-namespace Debug {
- template
- void die(std::format_string fmt, Args&&... args) {
- const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...));
-
- std::cout << "\n[ERR] " << logMsg << "\n";
- exit(1);
- }
-};
\ No newline at end of file
diff --git a/hyprpm/src/helpers/Sys.cpp b/hyprpm/src/helpers/Sys.cpp
deleted file mode 100644
index e9dd4c85..00000000
--- a/hyprpm/src/helpers/Sys.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-#include "Sys.hpp"
-#include "Die.hpp"
-#include "StringUtils.hpp"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-using namespace Hyprutils::OS;
-using namespace Hyprutils::String;
-
-inline constexpr std::array SUPERUSER_BINARIES = {
- "sudo",
- "doas",
- "run0",
-};
-
-static std::string validSubinsAsStr() {
- std::ostringstream oss;
- auto it = SUPERUSER_BINARIES.begin();
- if (it != SUPERUSER_BINARIES.end()) {
- oss << *it++;
- for (; it != SUPERUSER_BINARIES.end(); ++it)
- oss << ", " << *it;
- }
-
- return oss.str();
-}
-
-static bool executableExistsInPath(const std::string& exe) {
- return NSys::findInPath(exe).has_value();
-}
-
-std::optional NSys::findInPath(const std::string& exe) {
- const char* PATHENV = std::getenv("PATH");
- if (!PATHENV)
- return std::nullopt;
-
- CVarList paths(PATHENV, 0, ':', true);
- std::error_code ec;
-
- for (const auto& PATH : paths) {
- std::filesystem::path candidate = std::filesystem::path(PATH) / exe;
- if (!std::filesystem::exists(candidate, ec) || ec)
- continue;
- if (!std::filesystem::is_regular_file(candidate, ec) || ec)
- continue;
- auto perms = std::filesystem::status(candidate, ec).permissions();
- if (ec)
- continue;
- if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
- return candidate.string();
- }
-
- return std::nullopt;
-}
-
-static std::string subin() {
- static std::string bin;
- static bool once = true;
- if (!once)
- return bin;
-
- for (const auto& BIN : SUPERUSER_BINARIES) {
- if (!executableExistsInPath(std::string{BIN}))
- continue;
-
- bin = BIN;
- break;
- }
-
- once = false;
-
- if (bin.empty())
- Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr()));
-
- return bin;
-}
-
-static bool verifyStringValid(const std::string& s) {
- return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; });
-}
-
-int NSys::getUID() {
- const auto UID = getuid();
- const auto PWUID = getpwuid(UID);
- return PWUID ? PWUID->pw_uid : UID;
-}
-
-int NSys::getEUID() {
- const auto UID = geteuid();
- const auto PWUID = getpwuid(UID);
- return PWUID ? PWUID->pw_uid : UID;
-}
-
-bool NSys::isSuperuser() {
- return getuid() != geteuid() || geteuid() == 0;
-}
-
-void NSys::root::cacheSudo() {
- // "caches" the sudo so that the prompt later doesn't pop up in a weird spot
- // sudo will not ask us again for a moment
- CProcess proc(subin(), {"echo", "hyprland"});
- proc.runSync();
-}
-
-void NSys::root::dropSudo() {
- if (subin() != "sudo") {
- std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin()));
- return;
- }
-
- CProcess proc(subin(), {"-k"});
- proc.runSync();
-}
-
-bool NSys::root::createDirectory(const std::string& path, const std::string& mode) {
- if (!verifyStringValid(path))
- return false;
-
- if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
- return false;
-
- CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path});
-
- return proc.runSync() && proc.exitCode() == 0;
-}
-
-bool NSys::root::removeRecursive(const std::string& path) {
- if (!verifyStringValid(path))
- return false;
-
- std::error_code ec;
- const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec);
-
- if (ec)
- return false;
-
- if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm"))
- return false;
-
- CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE});
-
- return proc.runSync() && proc.exitCode() == 0;
-}
-
-bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) {
- if (!verifyStringValid(what) || !verifyStringValid(where))
- return false;
-
- if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
- return false;
-
- CProcess proc(subin(), {"install", "-m" + mode, "-o", "0", "-g", "0", what, where});
-
- return proc.runSync() && proc.exitCode() == 0;
-}
-
-std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) {
- CProcess proc(subin(), {"/bin/sh", "-c", cmd});
-
- if (!proc.runSync())
- return "";
-
- return proc.stdOut();
-}
diff --git a/hyprpm/src/helpers/Sys.hpp b/hyprpm/src/helpers/Sys.hpp
deleted file mode 100644
index 03d5a0de..00000000
--- a/hyprpm/src/helpers/Sys.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include
-#include
-
-namespace NSys {
- bool isSuperuser();
- int getUID();
- int getEUID();
- std::optional findInPath(const std::string& exe);
-
- // NOLINTNEXTLINE
- namespace root {
- void cacheSudo();
- void dropSudo();
-
- //
- [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool createDirectory(const std::string& path, const std::string& mode);
- [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool removeRecursive(const std::string& path);
- [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool install(const std::string& what, const std::string& where, const std::string& mode);
-
- // Do not use this unless absolutely necessary!
- std::string runAsSuperuserUnsafe(const std::string& cmd);
- };
-};
diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp
index f5e14bbb..0d5efd1d 100644
--- a/hyprpm/src/main.cpp
+++ b/hyprpm/src/main.cpp
@@ -2,37 +2,34 @@
#include "helpers/StringUtils.hpp"
#include "core/PluginManager.hpp"
#include "core/DataState.hpp"
-#include "helpers/Sys.hpp"
+#include
#include
#include
#include
-
-#include
-using namespace Hyprutils::Utils;
+#include
+#include
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
┃
-┣ add [git rev] → Install a new plugin repository from git. Git revision
-┃ is optional, when set, commit locks are ignored.
-┣ remove → Remove an installed plugin repository.
-┣ enable → Enable a plugin.
-┣ disable → Disable a plugin.
-┣ update → Check and update all plugins if needed.
-┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
-┣ list → List all installed plugins.
-┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers.
+┣ add [url] [git rev] → Install a new plugin repository from git. Git revision
+┃ is optional, when set, commit locks are ignored.
+┣ remove [url/name] → Remove an installed plugin repository
+┣ enable [name] → Enable a plugin
+┣ disable [name] → Disable a plugin
+┣ update → Check and update all plugins if needed
+┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
+┣ list → List all installed plugins
┃
┣ Flags:
┃
-┣ --no-nix | → Disable `nix develop` for build commands, even if Hyprland is nix.
-┣ --notify | -n → Send a hyprland notification confirming successful plugin load.
-┃ Warnings/Errors trigger notifications regardless of this flag.
-┣ --help | -h → Show this menu.
-┣ --verbose | -v → Enable too much logging.
-┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
-┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources.
-┣ --hl-url | → Pass a custom hyprland source url.
+┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events)
+┣ --notify-fail | -nn → Send a hyprland notification for fail events only
+┣ --help | -h → Show this menu
+┣ --verbose | -v → Enable too much logging
+┣ --force | -f → Force an operation ignoring checks (e.g. update -f)
+┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources
+┣ --hl-url | → Pass a custom hyprland source url
┗
)#";
@@ -48,7 +45,7 @@ int main(int argc, char** argv, char** envp) {
}
std::vector command;
- bool notify = false, verbose = false, force = false, noShallow = false, noNix = false;
+ bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
std::string customHlUrl;
for (int i = 1; i < argc; ++i) {
@@ -59,13 +56,9 @@ int main(int argc, char** argv, char** envp) {
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true;
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
- // TODO: Deprecated since v.053.0. Remove in version>0.56.0
- std::println(stderr, "{}", failureString("Deprececated flag."));
- g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] -n flag is deprecated, see hyprpm --help.");
+ notifyFail = notify = true;
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true;
- } else if (ARGS[i] == "--no-nix") {
- noNix = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
noShallow = true;
} else if (ARGS[i] == "--hl-url") {
@@ -94,9 +87,7 @@ int main(int argc, char** argv, char** envp) {
g_pPluginManager = std::make_unique();
g_pPluginManager->m_bVerbose = verbose;
g_pPluginManager->m_bNoShallow = noShallow;
- g_pPluginManager->m_bNoNix = noNix;
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
- g_pPluginManager->m_szArgv0 = argv[0];
if (command[0] == "add") {
if (command.size() < 2) {
@@ -105,44 +96,25 @@ int main(int argc, char** argv, char** envp) {
}
std::string rev = "";
- if (command.size() >= 3)
+ if (command.size() >= 3) {
rev = command[2];
-
- const auto HLVER = g_pPluginManager->getHyprlandVersion();
- auto GLOBALSTATE = DataState::getGlobalState();
-
- if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) {
- std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
- return 1;
}
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
-
- g_pPluginManager->updateHeaders(false);
-
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
} else if (command[0] == "remove") {
- if (command.size() < 2) {
+ if (ARGS.size() < 2) {
std::println(stderr, "{}", failureString("Not enough args for remove."));
return 1;
}
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
-
- return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1;
+ return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
} else if (command[0] == "update") {
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
-
- bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
- bool headers = g_pPluginManager->updateHeaders(force);
-
+ bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
+ bool headers = g_pPluginManager->updateHeaders(force);
if (headers) {
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
auto GLOBALSTATE = DataState::getGlobalState();
- const auto COMPILEDOUTDATED = HLVER.abiHash != GLOBALSTATE.headersAbiCompiled;
+ const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
@@ -156,24 +128,20 @@ int main(int argc, char** argv, char** envp) {
if (ret2 != LOADSTATE_OK)
return 1;
- } else {
+ } else if (notify)
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
- }
} else if (command[0] == "enable") {
- if (command.size() < 2) {
+ if (ARGS.size() < 2) {
std::println(stderr, "{}", failureString("Not enough args for enable."));
return 1;
}
- if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
+ if (!g_pPluginManager->enablePlugin(command[1])) {
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
return 1;
}
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
-
- auto ret = g_pPluginManager->ensurePluginsLoadState();
+ auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret == LOADSTATE_HYPRLAND_UPDATED)
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland.");
@@ -186,39 +154,33 @@ int main(int argc, char** argv, char** envp) {
return 1;
}
- if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
+ if (!g_pPluginManager->disablePlugin(command[1])) {
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
return 1;
}
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
-
- auto ret = g_pPluginManager->ensurePluginsLoadState();
-
+ auto ret = g_pPluginManager->ensurePluginsLoadState();
if (ret != LOADSTATE_OK)
return 1;
} else if (command[0] == "reload") {
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK) {
- switch (ret) {
- case LOADSTATE_FAIL:
- case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
- case LOADSTATE_HEADERS_OUTDATED:
- g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
- break;
- default: break;
+ if (notify) {
+ switch (ret) {
+ case LOADSTATE_FAIL:
+ case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
+ case LOADSTATE_HEADERS_OUTDATED:
+ g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
+ break;
+ default: break;
+ }
}
return 1;
- } else if (notify) {
+ } else if (notify && !notifyFail) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}
- } else if (command[0] == "purge-cache") {
- NSys::root::cacheSudo();
- CScopeGuard x([] { NSys::root::dropSudo(); });
- DataState::purgeAllCache();
} else if (command[0] == "list") {
g_pPluginManager->listAllPlugins();
} else {
diff --git a/hyprpm/src/meson.build b/hyprpm/src/meson.build
new file mode 100644
index 00000000..fd914f9d
--- /dev/null
+++ b/hyprpm/src/meson.build
@@ -0,0 +1,32 @@
+globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
+src = globber.stdout().strip().split('\n')
+
+executable(
+ 'hyprpm',
+ src,
+ dependencies: [
+ dependency('hyprutils', version: '>= 0.1.1'),
+ dependency('threads'),
+ dependency('tomlplusplus'),
+ dependency('glaze', method: 'cmake'),
+ ],
+ install: true,
+)
+
+install_data(
+ '../hyprpm.bash',
+ install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
+ install_tag: 'runtime',
+ rename: 'hyprpm',
+)
+install_data(
+ '../hyprpm.fish',
+ install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
+ install_tag: 'runtime',
+)
+install_data(
+ '../hyprpm.zsh',
+ install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
+ install_tag: 'runtime',
+ rename: '_hyprpm',
+)
diff --git a/hyprpm/src/progress/CProgressBar.cpp b/hyprpm/src/progress/CProgressBar.cpp
index 19a14d9c..9f2df08a 100644
--- a/hyprpm/src/progress/CProgressBar.cpp
+++ b/hyprpm/src/progress/CProgressBar.cpp
@@ -1,80 +1,82 @@
#include "CProgressBar.hpp"
#include
-#include
+#include
#include
#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
+#include
+
#include "../helpers/Colors.hpp"
-using namespace Hyprutils::Memory;
-
-static winsize getTerminalSize() {
- winsize w{};
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- return w;
-}
-
-static void clearCurrentLine() {
- std::print("\r\33[2K"); // ansi escape sequence to clear entire line
-}
-
void CProgressBar::printMessageAbove(const std::string& msg) {
- clearCurrentLine();
- std::print("\r{}\n", msg);
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- print(); // reprint bar underneath
+ std::string spaces;
+ spaces.reserve(w.ws_col);
+ for (size_t i = 0; i < w.ws_col; ++i) {
+ spaces += ' ';
+ }
+
+ std::println("\r{}\r{}", spaces, msg);
+ print();
}
void CProgressBar::print() {
- const auto w = getTerminalSize();
+ struct winsize w;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- if (m_bFirstPrint) {
+ if (m_bFirstPrint)
std::print("\n");
- m_bFirstPrint = false;
+ m_bFirstPrint = false;
+
+ std::string spaces;
+ spaces.reserve(w.ws_col);
+ for (size_t i = 0; i < w.ws_col; ++i) {
+ spaces += ' ';
}
- clearCurrentLine();
+ std::print("\r{}\r", spaces);
- float percentDone = 0.0f;
- if (m_fPercentage >= 0.0f)
+ std::string message = "";
+
+ float percentDone = 0;
+ if (m_fPercentage >= 0)
percentDone = m_fPercentage;
- else {
- // check for divide-by-zero
- percentDone = m_iMaxSteps > 0 ? sc(m_iSteps) / m_iMaxSteps : 0.0f;
+ else
+ percentDone = (float)m_iSteps / (float)m_iMaxSteps;
+
+ const auto BARWIDTH = std::clamp(w.ws_col - static_cast(m_szCurrentMessage.length()) - 2, 0UL, 50UL);
+
+ // draw bar
+ message += std::string{" "} + Colors::GREEN;
+ size_t i = 0;
+ for (; i < std::floor(percentDone * BARWIDTH); ++i) {
+ message += "━";
}
- // clamp to ensure no overflows (sanity check)
- percentDone = std::clamp(percentDone, 0.0f, 1.0f);
-
- const size_t BARWIDTH = std::clamp(w.ws_col - m_szCurrentMessage.length() - 2, 0, 50);
-
- std::ostringstream oss;
- oss << ' ' << Colors::GREEN;
-
- size_t filled = std::floor(percentDone * BARWIDTH);
- size_t i = 0;
-
- for (; i < filled; ++i)
- oss << "━";
if (i < BARWIDTH) {
- oss << "╍" << Colors::RESET;
- ++i;
- for (; i < BARWIDTH; ++i)
- oss << "━";
+ i++;
+
+ message += std::string{"╍"} + Colors::RESET;
+
+ for (; i < BARWIDTH; ++i) {
+ message += "━";
+ }
} else
- oss << Colors::RESET;
+ message += Colors::RESET;
- if (m_fPercentage >= 0.0f)
- oss << " " << std::format("{}%", sc(percentDone * 100.0)) << ' ';
+ // draw progress
+ if (m_fPercentage >= 0)
+ message += " " + std::format("{}%", static_cast(percentDone * 100.0)) + " ";
else
- oss << " " << std::format("{} / {}", m_iSteps, m_iMaxSteps) << ' ';
+ message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
+
+ // draw message
+ std::print("{} {}", message, m_szCurrentMessage);
- std::print("{} {}", oss.str(), m_szCurrentMessage);
std::fflush(stdout);
}
diff --git a/hyprtester/CMakeLists.txt b/hyprtester/CMakeLists.txt
deleted file mode 100644
index f17f73b1..00000000
--- a/hyprtester/CMakeLists.txt
+++ /dev/null
@@ -1,104 +0,0 @@
-cmake_minimum_required(VERSION 3.19)
-
-project(hyprtester DESCRIPTION "Hyprland test suite")
-
-include(GNUInstallDirs)
-
-set(CMAKE_CXX_STANDARD 26)
-set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
-
-find_package(PkgConfig REQUIRED)
-
-pkg_check_modules(hyprtester_deps REQUIRED IMPORTED_TARGET hyprutils>=0.5.0)
-
-file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
-
-add_executable(hyprtester ${SRCFILES})
-add_custom_command(
- TARGET hyprtester
- POST_BUILD
- COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/plugin/build.sh
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/plugin)
-
-target_link_libraries(hyprtester PUBLIC PkgConfig::hyprtester_deps)
-
-install(TARGETS hyprtester)
-
-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test.conf
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
-
-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin/hyprtestplugin.so
- DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
-
-file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/src/tests/clients/build.hpp
- "#include \n"
- "static const std::string binaryDir = \"${CMAKE_CURRENT_BINARY_DIR}\";"
-)
-
-######## wayland protocols testing stuff
-
-if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
- set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
-endif()
-
-find_package(hyprwayland-scanner 0.4.0 REQUIRED)
-pkg_check_modules(
- protocols_deps
- REQUIRED
- IMPORTED_TARGET
- hyprutils>=0.8.0
- wayland-client
- wayland-protocols
-)
-
-pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
-message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
-pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
-message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
-
-# gen core wayland stuff
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp
- COMMAND hyprwayland-scanner --wayland-enums --client
- ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_CURRENT_SOURCE_DIR}/protocols/
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
-
-function(protocolNew protoPath protoName external)
- if(external)
- set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
- else()
- set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
- endif()
-
- add_custom_command(
- OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp
- COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
- ${CMAKE_CURRENT_SOURCE_DIR}/protocols/
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
-endfunction()
-function(clientNew sourceName)
- cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "PROTOS")
-
- add_executable(${sourceName} clients/${sourceName}.cpp)
-
- target_include_directories(${sourceName} BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/protocols")
- target_link_libraries(${sourceName} PUBLIC PkgConfig::protocols_deps)
-
- target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp)
-
- foreach(protoName IN LISTS ARG_PROTOS)
- target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp)
- endforeach()
-endfunction()
-
-protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
-protocolnew("stable/xdg-shell" "xdg-shell" false)
-protocolnew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false)
-
-clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell")
-clientNew("pointer-scroll" PROTOS "xdg-shell")
-clientNew("child-window" PROTOS "xdg-shell")
-clientNew("shortcut-inhibitor" PROTOS "xdg-shell" "keyboard-shortcuts-inhibit-unstable-v1")
diff --git a/hyprtester/clients/child-window.cpp b/hyprtester/clients/child-window.cpp
deleted file mode 100644
index 5f66be6b..00000000
--- a/hyprtester/clients/child-window.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-
-using Hyprutils::Math::Vector2D;
-using namespace Hyprutils::Memory;
-
-struct SWlState {
- wl_display* display;
- CSharedPointer registry;
-
- // protocols
- CSharedPointer wlCompositor;
- CSharedPointer wlSeat;
- CSharedPointer wlShm;
- CSharedPointer xdgShell;
-
- // shm/buffer stuff
- CSharedPointer shmPool;
- CSharedPointer shmBuf;
- CSharedPointer shmBuf2;
- int shmFd = 0;
- size_t shmBufSize = 0;
- bool xrgb8888_support = false;
-
- // surface/toplevel stuff
- CSharedPointer surf;
- CSharedPointer xdgSurf;
- CSharedPointer xdgToplevel;
- Vector2D geom;
-
- // pointer
- CSharedPointer pointer;
- uint32_t enterSerial = 0;
-};
-
-bool debug, shouldExit, started;
-
-template
-//NOLINTNEXTLINE
-static void clientLog(std::format_string fmt, Args&&... args) {
- std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
- std::println("{}", text);
- std::fflush(stdout);
-}
-
-template
-//NOLINTNEXTLINE
-static void debugLog(std::format_string fmt, Args&&... args) {
- std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
- if (!debug)
- return;
- std::println("{}", text);
- std::fflush(stdout);
-}
-
-static bool bindRegistry(SWlState& state) {
- state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display));
-
- state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
- const std::string NAME = name;
- if (NAME == "wl_compositor") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6));
- } else if (NAME == "wl_shm") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1));
- } else if (NAME == "wl_seat") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9));
- } else if (NAME == "xdg_wm_base") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1));
- }
- });
- state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); });
-
- wl_display_roundtrip(state.display);
-
- if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) {
- clientLog("Failed to get protocols from Hyprland");
- return false;
- }
-
- return true;
-}
-
-static bool createShm(SWlState& state, Vector2D geom) {
- if (!state.xrgb8888_support)
- return false;
-
- size_t stride = geom.x * 4;
- size_t size = geom.y * stride;
- if (!state.shmPool) {
- const char* name = "/wl-shm-pointer-warp";
- state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (state.shmFd < 0)
- return false;
-
- if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size * 2) < 0) {
- close(state.shmFd);
- return false;
- }
-
- state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size * 2));
- if (!state.shmPool->resource()) {
- close(state.shmFd);
- state.shmFd = -1;
- state.shmPool.reset();
- return false;
- }
- state.shmBufSize = size;
- } else if (size > state.shmBufSize) {
- if (ftruncate(state.shmFd, size) < 0) {
- close(state.shmFd);
- state.shmFd = -1;
- state.shmPool.reset();
- return false;
- }
-
- state.shmPool->sendResize(size * 2);
- state.shmBufSize = size;
- }
-
- auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888));
- if (!buf->resource())
- return false;
-
- if (state.shmBuf) {
- state.shmBuf->sendDestroy();
- state.shmBuf.reset();
- }
- state.shmBuf = buf;
-
- return true;
-}
-
-static bool setupToplevel(SWlState& state) {
- state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) {
- if (format == WL_SHM_FORMAT_XRGB8888)
- state.xrgb8888_support = true;
- });
-
- state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); });
-
- state.surf = makeShared(state.wlCompositor->sendCreateSurface());
- if (!state.surf->resource())
- return false;
-
- state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource()));
- if (!state.xdgSurf->resource())
- return false;
-
- state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel());
- if (!state.xdgToplevel->resource())
- return false;
-
- state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); });
-
- state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
- state.geom = {1280, 720};
-
- if (!createShm(state, state.geom))
- exit(-1);
- });
-
- state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
- if (!state.shmBuf)
- debugLog("xdgSurf configure but no buf made yet?");
-
- state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
- state.surf->sendAttach(state.shmBuf.get(), 0, 0);
- state.surf->sendCommit();
-
- state.xdgSurf->sendAckConfigure(serial);
-
- if (!started) {
- started = true;
- clientLog("started");
- }
- });
-
- state.xdgToplevel->sendSetTitle("child-test parent");
- state.xdgToplevel->sendSetAppId("child-test-parent");
-
- state.surf->sendAttach(nullptr, 0, 0);
- state.surf->sendCommit();
-
- return true;
-}
-
-static bool setupSeat(SWlState& state) {
- state.pointer = makeShared(state.wlSeat->sendGetPointer());
- if (!state.pointer->resource())
- return false;
-
- state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) {
- debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y);
- state.enterSerial = serial;
- });
-
- state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); });
-
- state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); });
-
- return true;
-}
-
-struct SChildWindow {
- CSharedPointer surface;
- CSharedPointer xSurface;
- CSharedPointer toplevel;
-};
-
-static void parseRequest(SWlState& state, std::string str, SChildWindow& window) {
- if (str.starts_with("exit")) {
- shouldExit = true;
- return;
- }
-
- size_t index = str.find_first_of('\n');
- str = str.substr(0, index);
-
- if (str == "toplevel") {
- window.surface = makeShared(state.wlCompositor->sendCreateSurface());
- window.xSurface = makeShared(state.xdgShell->sendGetXdgSurface(window.surface->resource()));
-
- window.xSurface->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
- if (!state.shmBuf)
- debugLog("xdgSurf configure but no buf made yet?");
-
- window.xSurface->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
- window.surface->sendAttach(state.shmBuf2.get(), 0, 0);
- window.surface->sendCommit();
-
- window.xSurface->sendAckConfigure(serial);
- });
-
- window.toplevel = makeShared(window.xSurface->sendGetToplevel());
-
- window.toplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
- size_t stride = 1280 * 4;
- size_t size = 720 * stride;
-
- auto buf = makeShared(state.shmPool->sendCreateBuffer(size, state.geom.x, state.geom.y, stride, WL_SHM_FORMAT_XRGB8888));
- if (!buf->resource())
- clientLog("Failed to create child buffer");
-
- if (state.shmBuf2) {
- state.shmBuf2->sendDestroy();
- state.shmBuf2.reset();
- }
- state.shmBuf2 = buf;
- });
-
- window.toplevel->sendSetTitle("child-test child");
- window.toplevel->sendSetAppId("child-test-child");
- window.toplevel->sendSetParent(state.xdgToplevel.get());
-
- window.surface->sendAttach(nullptr, 0, 0);
- window.surface->sendCommit();
- clientLog("child started");
- return;
- }
-}
-
-int main(int argc, char** argv) {
- if (argc != 1 && argc != 2)
- clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs.");
-
- if (argc == 2 && std::string{argv[1]} == "--debug")
- debug = true;
-
- SWlState state;
- SChildWindow window;
-
- // WAYLAND_DISPLAY env should be set to the correct one
- state.display = wl_display_connect(nullptr);
- if (!state.display) {
- clientLog("Failed to connect to wayland display");
- return -1;
- }
-
- if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state))
- return -1;
-
- std::array readBuf;
- readBuf.fill(0);
-
- wl_display_flush(state.display);
-
- struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}};
- while (!shouldExit && poll(fds, 2, 0) != -1) {
- if (fds[0].revents & POLLIN) {
- wl_display_flush(state.display);
-
- if (wl_display_prepare_read(state.display) == 0) {
- wl_display_read_events(state.display);
- wl_display_dispatch_pending(state.display);
- } else
- wl_display_dispatch(state.display);
-
- int ret = 0;
- do {
- ret = wl_display_dispatch_pending(state.display);
- wl_display_flush(state.display);
- } while (ret > 0);
- }
-
- if (fds[1].revents & POLLIN) {
- ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023);
- if (bytesRead == -1)
- continue;
- readBuf[bytesRead] = 0;
-
- parseRequest(state, std::string{readBuf.data()}, window);
- }
- }
-
- wl_display* display = state.display;
- state = {};
- window = {};
-
- wl_display_disconnect(display);
- return 0;
-}
diff --git a/hyprtester/clients/pointer-scroll.cpp b/hyprtester/clients/pointer-scroll.cpp
deleted file mode 100644
index 59120961..00000000
--- a/hyprtester/clients/pointer-scroll.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-using Hyprutils::Math::Vector2D;
-using namespace Hyprutils::Memory;
-
-struct SWlState {
- wl_display* display;
- CSharedPointer registry;
-
- // protocols
- CSharedPointer wlCompositor;
- CSharedPointer wlSeat;
- CSharedPointer wlShm;
- CSharedPointer xdgShell;
-
- // shm/buffer stuff
- CSharedPointer shmPool;
- CSharedPointer shmBuf;
- int shmFd;
- size_t shmBufSize;
- bool xrgb8888_support = false;
-
- // surface/toplevel stuff
- CSharedPointer surf;
- CSharedPointer xdgSurf;
- CSharedPointer xdgToplevel;
- Vector2D geom;
-
- // pointer
- CSharedPointer pointer;
- uint32_t enterSerial;
-
- // last delta
- float lastScrollDelta = -1.F;
- bool writeDelta = false;
-};
-
-static std::ofstream logfile;
-
-static bool debug, started, shouldExit;
-
-template
-//NOLINTNEXTLINE
-static void clientLog(std::format_string fmt, Args&&... args) {
- std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
- std::println("{}", text);
- logfile << text << std::endl;
- std::fflush(stdout);
-}
-
-template
-//NOLINTNEXTLINE
-static void debugLog(std::format_string fmt, Args&&... args) {
- std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
- logfile << text << std::endl;
- if (!debug)
- return;
- std::println("{}", text);
- std::fflush(stdout);
-}
-
-static bool bindRegistry(SWlState& state) {
- state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display));
-
- state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
- const std::string NAME = name;
- if (NAME == "wl_compositor") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6));
- } else if (NAME == "wl_shm") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1));
- } else if (NAME == "wl_seat") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9));
- } else if (NAME == "xdg_wm_base") {
- debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
- state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1));
- }
- });
- state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); });
-
- wl_display_roundtrip(state.display);
-
- if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) {
- clientLog("Failed to get protocols from Hyprland");
- return false;
- }
-
- return true;
-}
-
-static bool createShm(SWlState& state, Vector2D geom) {
- if (!state.xrgb8888_support)
- return false;
-
- size_t stride = geom.x * 4;
- size_t size = geom.y * stride;
- if (!state.shmPool) {
- const char* name = "/wl-shm-pointer-scroll";
- state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (state.shmFd < 0)
- return false;
-
- if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size) < 0) {
- close(state.shmFd);
- return false;
- }
-
- state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size));
- if (!state.shmPool->resource()) {
- close(state.shmFd);
- state.shmFd = -1;
- state.shmPool.reset();
- return false;
- }
- state.shmBufSize = size;
- } else if (size > state.shmBufSize) {
- if (ftruncate(state.shmFd, size) < 0) {
- close(state.shmFd);
- state.shmFd = -1;
- state.shmPool.reset();
- return false;
- }
-
- state.shmPool->sendResize(size);
- state.shmBufSize = size;
- }
-
- auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888));
- if (!buf->resource())
- return false;
-
- if (state.shmBuf) {
- state.shmBuf->sendDestroy();
- state.shmBuf.reset();
- }
-
- state.shmBuf = buf;
-
- return true;
-}
-
-static bool setupToplevel(SWlState& state) {
- state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) {
- if (format == WL_SHM_FORMAT_XRGB8888)
- state.xrgb8888_support = true;
- });
-
- state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); });
-
- state.surf = makeShared(state.wlCompositor->sendCreateSurface());
- if (!state.surf->resource())
- return false;
-
- state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource()));
- if (!state.xdgSurf->resource())
- return false;
-
- state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel());
- if (!state.xdgToplevel->resource())
- return false;
-
- state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); });
-
- state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
- state.geom = {1280, 720};
-
- if (!createShm(state, state.geom))
- exit(-1);
- });
-
- state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
- if (!state.shmBuf)
- debugLog("xdgSurf configure but no buf made yet?");
-
- state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
- state.surf->sendAttach(state.shmBuf.get(), 0, 0);
- state.surf->sendCommit();
-
- state.xdgSurf->sendAckConfigure(serial);
-
- if (!started) {
- started = true;
- clientLog("started");
- }
- });
-
- state.xdgToplevel->sendSetTitle("pointer-scroll test client");
- state.xdgToplevel->sendSetAppId("pointer-scroll");
-
- state.surf->sendAttach(nullptr, 0, 0);
- state.surf->sendCommit();
-
- return true;
-}
-
-static bool setupSeat(SWlState& state) {
- state.pointer = makeShared