mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-07-31 04:01:56 -07:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9f038741a4 |
118
.github/ISSUE_TEMPLATE/bug.yml
vendored
118
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -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: "<details>
|
||||
<summary>System/Version info</summary>
|
||||
|
||||
|
||||
```
|
||||
|
||||
<Paste the output of the command here, without removing any formatting around this>
|
||||
|
||||
```
|
||||
|
||||
|
||||
</details>"
|
||||
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.
|
||||
|
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
@@ -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
|
||||
|
10
.github/actions/setup_base/action.yml
vendored
10
.github/actions/setup_base/action.yml
vendored
@@ -20,7 +20,6 @@ runs:
|
||||
clang \
|
||||
cmake \
|
||||
git \
|
||||
glaze \
|
||||
glm \
|
||||
glslang \
|
||||
go \
|
||||
@@ -65,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: |
|
||||
|
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Build Hyprland
|
||||
run: |
|
||||
CFLAGS=-Werror CXXFLAGS=-Werror make all
|
||||
make all
|
||||
|
||||
- name: Compress and package artifacts
|
||||
run: |
|
||||
|
101
.github/workflows/close-issues.yml
vendored
101
.github/workflows/close-issues.yml
vendored
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
28
.github/workflows/nix-build.yml
vendored
Normal file
28
.github/workflows/nix-build.yml
vendored
Normal file
@@ -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"
|
22
.github/workflows/nix-ci.yml
vendored
22
.github/workflows/nix-ci.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Nix
|
||||
name: Nix (CI)
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
@@ -8,23 +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:hyprwm/Hyprland?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:hyprwm/Hyprland?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)
|
||||
needs: hyprland
|
||||
uses: ./.github/workflows/nix-test.yml
|
||||
uses: ./.github/workflows/nix-build.yml
|
||||
secrets: inherit
|
||||
|
59
.github/workflows/nix-test.yml
vendored
59
.github/workflows/nix-test.yml
vendored
@@ -1,59 +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
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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
|
||||
# 1G = 1073741824
|
||||
gc-max-store-size-linux: 5G
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}
|
||||
# created more than this number of seconds ago
|
||||
purge-created: 0
|
||||
# or, last accessed more than this number of seconds ago
|
||||
# relative to the start of the `Post Restore and save Nix store` phase
|
||||
purge-last-accessed: 0
|
||||
# except any version with the key that is the same as the `primary-key`
|
||||
purge-primary-key: never
|
||||
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: hyprland
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Run test VM
|
||||
run: nix build 'github:hyprwm/Hyprland?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
|
31
.github/workflows/nix-update-inputs.yml
vendored
31
.github/workflows/nix-update-inputs.yml
vendored
@@ -17,36 +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
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||
# 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
|
||||
# 1G = 1073741824
|
||||
gc-max-store-size-linux: 1G
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago
|
||||
purge-created: 0
|
||||
# or, last accessed more than this number of seconds ago
|
||||
# relative to the start of the `Post Restore and save Nix store` phase
|
||||
purge-last-accessed: 0
|
||||
# except any version with the key that is the same as the `primary-key`
|
||||
purge-primary-key: never
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- name: Update inputs
|
||||
run: nix/update-inputs.sh
|
||||
|
||||
|
53
.github/workflows/nix.yml
vendored
53
.github/workflows/nix.yml
vendored
@@ -1,53 +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
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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
|
||||
# 1G = 1073741824
|
||||
gc-max-store-size-linux: 5G
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}
|
||||
# created more than this number of seconds ago
|
||||
purge-created: 0
|
||||
# or, last accessed more than this number of seconds ago
|
||||
# relative to the start of the `Post Restore and save Nix store` phase
|
||||
purge-last-accessed: 0
|
||||
# except any version with the key that is the same as the `primary-key`
|
||||
purge-primary-key: never
|
||||
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: hyprland
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- run: ${{ inputs.command }}
|
28
.github/workflows/stale.yml
vendored
Normal file
28
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "7 */4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
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
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,8 +28,6 @@ protocols/*.c*
|
||||
protocols/*.h*
|
||||
.ccls-cache
|
||||
*.so
|
||||
src/render/shaders/*.inc
|
||||
src/render/shaders/Shaders.hpp
|
||||
|
||||
hyprctl/hyprctl
|
||||
|
||||
|
@@ -9,7 +9,6 @@ project(
|
||||
DESCRIPTION "A Modern C++ Wayland Compositor"
|
||||
VERSION ${VER})
|
||||
|
||||
include(CTest)
|
||||
include(CheckIncludeFile)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -26,9 +25,6 @@ message(STATUS "Gathering git info")
|
||||
# Get git info hash and branch
|
||||
execute_process(COMMAND ./scripts/generateVersion.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
# Make shader files includable
|
||||
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
@@ -90,8 +86,6 @@ add_compile_options(
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-narrowing
|
||||
-Wno-pointer-arith
|
||||
-Wno-clobbered
|
||||
-Wpedantic
|
||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||
|
||||
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
|
||||
@@ -101,14 +95,18 @@ 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})
|
||||
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.0)
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.4.5)
|
||||
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.8.1)
|
||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.3)
|
||||
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)
|
||||
@@ -131,14 +129,14 @@ pkg_check_modules(
|
||||
xkbcommon
|
||||
uuid
|
||||
wayland-server>=1.22.90
|
||||
wayland-protocols>=1.43
|
||||
wayland-protocols>=1.41
|
||||
cairo
|
||||
pango
|
||||
pangocairo
|
||||
pixman-1
|
||||
xcursor
|
||||
libdrm
|
||||
libinput>=1.28
|
||||
libinput
|
||||
gbm
|
||||
gio-2.0
|
||||
re2)
|
||||
@@ -155,7 +153,7 @@ endif()
|
||||
|
||||
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
|
||||
|
||||
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")
|
||||
@@ -214,6 +212,11 @@ if(NOT HAS_INOTIFY AND inotify_FOUND)
|
||||
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)
|
||||
message(STATUS "Using the NO_XWAYLAND flag, disabling XWayland!")
|
||||
add_compile_definitions(NO_XWAYLAND)
|
||||
@@ -310,7 +313,7 @@ endfunction()
|
||||
|
||||
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}")
|
||||
@@ -342,8 +345,6 @@ 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)
|
||||
@@ -378,9 +379,6 @@ 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)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
@@ -394,12 +392,6 @@ else()
|
||||
message(STATUS "hyprpm is enabled (NO_HYPRPM not defined)")
|
||||
endif()
|
||||
|
||||
if(NO_TESTS)
|
||||
message(STATUS "building tests is disabled")
|
||||
else()
|
||||
message(STATUS "building tests is enabled (NO_TESTS not defined)")
|
||||
endif()
|
||||
|
||||
# binary and symlink
|
||||
install(TARGETS Hyprland)
|
||||
|
||||
@@ -452,19 +444,4 @@ install(
|
||||
DIRECTORY ${HEADERS_SRC}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "*.inc")
|
||||
|
||||
if(TESTS)
|
||||
enable_testing()
|
||||
add_custom_target(tests)
|
||||
|
||||
add_subdirectory(hyprtester)
|
||||
add_test(
|
||||
NAME "Main Test"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hyprtester
|
||||
COMMAND hyprtester)
|
||||
|
||||
add_dependencies(tests hyprtester)
|
||||
endif()
|
||||
PATTERN "*.h*")
|
||||
|
10
Makefile
10
Makefile
@@ -3,6 +3,14 @@ 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`
|
||||
@@ -44,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
|
||||
|
14
README.md
14
README.md
@@ -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,9 +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/
|
||||
[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
|
||||
|
||||
|
||||
@@ -125,9 +125,9 @@ easy IPC, much more QoL stuff than other compositors and more...
|
||||
|
||||
<!----------------------------------{ Images }--------------------------------->
|
||||
|
||||
[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
|
||||
|
||||
|
||||
<!----------------------------------{ Badges }--------------------------------->
|
||||
|
32
SECURITY.md
32
SECURITY.md
@@ -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`
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 506 KiB |
@@ -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
|
||||
|
@@ -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/)
|
||||
|
||||
<br/>
|
||||
|
||||
# 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.
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -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 ~`
|
||||
|
@@ -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
|
||||
|
@@ -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,7 +22,7 @@ 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
|
||||
@@ -46,56 +46,39 @@ $menu = wofi --show drun
|
||||
### 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,11 +104,11 @@ decoration {
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#animations
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = yes, please :)
|
||||
|
||||
# Default animations, see https://wiki.hypr.land/Configuring/Animations/ for more
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
bezier = easeOutQuint,0.23,1,0.32,1
|
||||
bezier = easeInOutCubic,0.65,0.05,0.36,1
|
||||
@@ -151,28 +134,28 @@ animations {
|
||||
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 = 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]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = 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. :(
|
||||
@@ -183,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 =
|
||||
@@ -200,13 +183,13 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#gestures
|
||||
# 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
|
||||
@@ -217,10 +200,10 @@ 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, exit,
|
||||
@@ -277,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
|
||||
@@ -290,14 +273,17 @@ 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 windowrule
|
||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
|
72
flake.lock
generated
72
flake.lock
generated
@@ -16,11 +16,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751740947,
|
||||
"narHash": "sha256-35040CHH7P3JGmhGVfEb2oJHL/A5mI2IXumhkxrBnao=",
|
||||
"lastModified": 1741934125,
|
||||
"narHash": "sha256-qwI47l3aKXRpDvmCKDbLV70iVfAqhpuKqT7qYHA4KJk=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "dfc1db15a08c4cd234288f66e1199c653495301f",
|
||||
"rev": "bea48d0bbe15fb3d758a8b6be865836c97056575",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -79,11 +79,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749155331,
|
||||
"narHash": "sha256-XR9fsI0zwLiFWfqi/pdS/VD+YNorKb3XIykgTg4l1nA=",
|
||||
"lastModified": 1738664950,
|
||||
"narHash": "sha256-xIeGNM+iivwVHkv9tHwOqoUP5dDrtees34bbFKKMZYs=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"rev": "45fcc10b4c282746d93ec406a740c43b48b4ef80",
|
||||
"rev": "7c6d165e1eb9045a996551eb9f121b6d1b30adc3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -105,11 +105,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751808145,
|
||||
"narHash": "sha256-OXgL0XaKMmfX2rRQkt9SkJw+QNfv0jExlySt1D6O72g=",
|
||||
"lastModified": 1739049071,
|
||||
"narHash": "sha256-3+7TpXMrbsUXSwgr5VAKAnmkzMb6JO+Rvc9XRb5NMg4=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "b841473a0bd4a1a74a0b64f1ec2ab199035c349f",
|
||||
"rev": "175c6b29b6ff82100539e7c4363a35a02c74dd73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -128,11 +128,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749046714,
|
||||
"narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=",
|
||||
"lastModified": 1738422629,
|
||||
"narHash": "sha256-5v+bv75wJWvahyM2xcMTSNNxmV8a7hb01Eey5zYnBJw=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330",
|
||||
"rev": "755aef8dab49d0fc4663c715fa4ad221b2aedaed",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -157,11 +157,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749154592,
|
||||
"narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=",
|
||||
"lastModified": 1737634706,
|
||||
"narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074",
|
||||
"rev": "8810df502cdee755993cb803eba7b23f189db795",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -189,11 +189,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750371812,
|
||||
"narHash": "sha256-D868K1dVEACw17elVxRgXC6hOxY+54wIEjURztDWLk8=",
|
||||
"lastModified": 1739048983,
|
||||
"narHash": "sha256-REhTcXq4qs3B3cCDtLlYDz0GZvmsBSh947Ub6pQWGTQ=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"rev": "b13c7481e37856f322177010bdf75fccacd1adc8",
|
||||
"rev": "3504a293c8f8db4127cb0f7cfc1a318ffb4316f8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -215,11 +215,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750371198,
|
||||
"narHash": "sha256-/iuJ1paQOBoSLqHflRNNGyroqfF/yvPNurxzcCT0cAE=",
|
||||
"lastModified": 1741191527,
|
||||
"narHash": "sha256-kM+11Nch47Xwfgtw2EpRitJuORy4miwoMuRi5tyMBDY=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "cee01452bca58d6cadb3224e21e370de8bc20f0b",
|
||||
"rev": "72df3861f1197e41b078faa3e38eedd60e00018d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -238,11 +238,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751888065,
|
||||
"narHash": "sha256-F2SV9WGqgtRsXIdUrl3sRe0wXlQD+kRRZcSfbepjPJY=",
|
||||
"lastModified": 1741534688,
|
||||
"narHash": "sha256-EV3945SnjOCuRVbGRghsWx/9D89FyshnSO1Q6/TuQ14=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "a8229739cf36d159001cfc203871917b83fdf917",
|
||||
"rev": "dd1f720cbc2dbb3c71167c9598045dd3261d27b3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -261,11 +261,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751881472,
|
||||
"narHash": "sha256-meB0SnXbwIe2trD041MLKEv6R7NZ759QwBcVIhlSBfE=",
|
||||
"lastModified": 1739870480,
|
||||
"narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "8fb426b3e5452fd9169453fd6c10f8c14ca37120",
|
||||
"rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -276,11 +276,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1751792365,
|
||||
"narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=",
|
||||
"lastModified": 1741851582,
|
||||
"narHash": "sha256-cPfs8qMccim2RBgtKGF+x9IBCduRvd/N5F4nYpU0TVE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb",
|
||||
"rev": "6607cf789e541e7873d40d3a8f7815ea92204f32",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -299,11 +299,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1750779888,
|
||||
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
|
||||
"lastModified": 1741379162,
|
||||
"narHash": "sha256-srpAbmJapkaqGRE3ytf3bj4XshspVR5964OX5LfjDWc=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
|
||||
"rev": "b5a62751225b2f62ff3147d0a334055ebadcd5cc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -365,11 +365,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751300244,
|
||||
"narHash": "sha256-PFuv1TZVYvQhha0ac53E3YgdtmLShrN0t4T6xqHl0jE=",
|
||||
"lastModified": 1741934139,
|
||||
"narHash": "sha256-ZhTcTH9FoeAtbPfWGrhkH7RjLJZ7GeF18nygLAMR+WE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "6115f3fdcb2c1a57b4a80a69f3c797e47607b90a",
|
||||
"rev": "150b0b6f52bb422a1b232a53698606fe0320dde0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
23
flake.nix
23
flake.nix
@@ -102,21 +102,6 @@
|
||||
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;};
|
||||
|
||||
@@ -138,8 +123,7 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
// (import ./nix/tests inputs pkgsFor.${system}));
|
||||
});
|
||||
|
||||
packages = eachSystem (system: {
|
||||
default = self.packages.${system}.hyprland;
|
||||
@@ -147,14 +131,13 @@
|
||||
(pkgsFor.${system})
|
||||
# hyprland-packages
|
||||
hyprland
|
||||
hyprland-debug
|
||||
hyprland-legacy-renderer
|
||||
hyprland-unwrapped
|
||||
hyprtester
|
||||
# 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: {
|
||||
|
@@ -146,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:
|
||||
|
@@ -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)"
|
||||
|
@@ -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"
|
||||
|
@@ -106,7 +106,6 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
|
||||
| (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"
|
||||
|
@@ -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"
|
||||
|
129
hyprctl/main.cpp
129
hyprctl/main.cpp
@@ -14,9 +14,6 @@
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <ranges>
|
||||
#include <optional>
|
||||
#include <charconv>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
@@ -30,6 +27,8 @@ using namespace Hyprutils::String;
|
||||
|
||||
#include "Strings.hpp"
|
||||
|
||||
#define PAD
|
||||
|
||||
std::string instanceSignature;
|
||||
bool quiet = false;
|
||||
|
||||
@@ -38,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;
|
||||
|
||||
@@ -64,74 +64,47 @@ std::string getRuntimeDir() {
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
static std::optional<uint64_t> 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<SInstanceData> 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<SInstanceData> instances() {
|
||||
std::vector<SInstanceData> 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;
|
||||
}
|
||||
@@ -173,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) {
|
||||
@@ -199,8 +172,10 @@ 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";
|
||||
|
||||
@@ -211,7 +186,7 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||
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)");
|
||||
@@ -252,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) {
|
||||
@@ -268,7 +243,9 @@ 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);
|
||||
|
||||
@@ -280,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)");
|
||||
@@ -303,16 +280,16 @@ int requestIPC(std::string_view filename, std::string_view arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requestHyprpaper(std::string_view arg) {
|
||||
int requestHyprpaper(std::string arg) {
|
||||
return requestIPC(".hyprpaper.sock", arg);
|
||||
}
|
||||
|
||||
int requestHyprsunset(std::string_view 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/");
|
||||
@@ -383,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";
|
||||
|
@@ -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 QUIET)
|
||||
if (NOT glaze_FOUND)
|
||||
set(GLAZE_VERSION v5.1.1)
|
||||
set(GLAZE_VERSION v4.2.3)
|
||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
|
@@ -1,41 +1,22 @@
|
||||
#include "DataState.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <toml++/toml.hpp>
|
||||
#include <print>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#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/"};
|
||||
|
||||
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() {
|
||||
@@ -60,32 +41,21 @@ std::vector<std::filesystem::path> 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{
|
||||
@@ -98,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},
|
||||
@@ -112,16 +80,16 @@ 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 std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
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("");
|
||||
@@ -137,6 +105,8 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
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("");
|
||||
@@ -152,14 +122,7 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -168,13 +131,9 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
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{
|
||||
@@ -184,20 +143,17 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||
};
|
||||
// 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());
|
||||
@@ -212,6 +168,8 @@ SGlobalState DataState::getGlobalState() {
|
||||
std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::vector<SPluginRepository> repos;
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
@@ -247,6 +205,8 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
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) {
|
||||
@@ -264,11 +224,9 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
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;
|
||||
}
|
||||
@@ -276,18 +234,3 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
|
||||
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");
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ namespace DataState {
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
void purgeAllCache();
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
|
@@ -1,86 +0,0 @@
|
||||
#include "HyprlandSocket.hpp"
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
#include <print>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
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, (sockaddr*)&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;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NHyprlandSocket {
|
||||
std::string send(const std::string& cmd);
|
||||
};
|
@@ -1,12 +1,6 @@
|
||||
#include "Manifest.hpp"
|
||||
#include <toml++/toml.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
// 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 <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@@ -27,8 +27,8 @@ class CManifest {
|
||||
std::string name;
|
||||
std::vector<std::string> authors;
|
||||
std::vector<std::pair<std::string, std::string>> commitPins;
|
||||
} m_repository;
|
||||
} m_sRepository;
|
||||
|
||||
std::vector<SManifestPlugin> m_plugins;
|
||||
bool m_good = true;
|
||||
std::vector<SManifestPlugin> m_vPlugins;
|
||||
bool m_bGood = true;
|
||||
};
|
@@ -4,9 +4,6 @@
|
||||
#include "../progress/CProgressBar.hpp"
|
||||
#include "Manifest.hpp"
|
||||
#include "DataState.hpp"
|
||||
#include "HyprlandSocket.hpp"
|
||||
#include "../helpers/Sys.hpp"
|
||||
#include "../helpers/Die.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
@@ -52,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;
|
||||
@@ -76,7 +66,7 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||
else
|
||||
onceInstalled = true;
|
||||
|
||||
const auto HLVERCALL = running ? NHyprlandSocket::send("/version") : execAndGet("Hyprland --version");
|
||||
const auto HLVERCALL = running ? execAndGet("hyprctl version") : execAndGet("Hyprland --version");
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
|
||||
|
||||
@@ -139,8 +129,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
const auto HLVER = getHyprlandVersion();
|
||||
|
||||
if (!hasDeps()) {
|
||||
std::println(stderr, "\n{}",
|
||||
failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, 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;
|
||||
}
|
||||
|
||||
@@ -159,11 +148,6 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
}
|
||||
|
||||
if (GLOBALSTATE.headersHashCompiled.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 "
|
||||
@@ -241,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 + ", ";
|
||||
@@ -261,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;
|
||||
|
||||
@@ -289,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;
|
||||
}
|
||||
|
||||
@@ -298,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. */) {
|
||||
@@ -341,11 +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();
|
||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name;
|
||||
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_plugins) {
|
||||
for (auto const& p : pManifest->m_vPlugins) {
|
||||
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
|
||||
}
|
||||
DataState::addNewPluginRepo(repo);
|
||||
@@ -452,7 +435,7 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
const auto HLVER = getHyprlandVersion(false);
|
||||
|
||||
if (!hasDeps()) {
|
||||
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, 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;
|
||||
}
|
||||
|
||||
@@ -565,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));
|
||||
|
||||
@@ -593,14 +568,9 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||
progress.m_szCurrentMessage = "Done!";
|
||||
progress.print();
|
||||
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
||||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
|
||||
std::print("\n");
|
||||
} else {
|
||||
progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID)));
|
||||
progress.printMessageAbove(infoString("if the problem persists, try running hyprpm purge-cache."));
|
||||
progress.m_iSteps = 5;
|
||||
progress.m_szCurrentMessage = "Failed";
|
||||
progress.print();
|
||||
@@ -707,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;
|
||||
|
||||
@@ -727,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. */) {
|
||||
@@ -769,7 +739,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
newrepo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_plugins) {
|
||||
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});
|
||||
}
|
||||
@@ -826,9 +796,9 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
|
||||
}
|
||||
const auto HYPRPMPATH = DataState::getDataStatePath();
|
||||
|
||||
const auto json = glz::read_json<glz::json_t::array_t>(NHyprlandSocket::send("j/plugins list"));
|
||||
const auto json = glz::read_json<glz::json_t::array_t>(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;
|
||||
}
|
||||
|
||||
@@ -912,15 +882,14 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
||||
auto HLVER = getHyprlandVersion(true);
|
||||
|
||||
if (state.headersHashCompiled != HLVER.hash) {
|
||||
if (load)
|
||||
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled));
|
||||
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;
|
||||
}
|
||||
@@ -945,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((int)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) {
|
||||
@@ -978,15 +947,11 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
|
||||
}
|
||||
|
||||
bool CPluginManager::hasDeps() {
|
||||
bool hasAllDeps = true;
|
||||
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config", "g++", "gcc", "git"};
|
||||
|
||||
std::vector<std::string> 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;
|
||||
return true;
|
||||
}
|
||||
|
@@ -40,8 +40,6 @@ struct SHyprlandVersion {
|
||||
|
||||
class CPluginManager {
|
||||
public:
|
||||
CPluginManager();
|
||||
|
||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||
bool removePluginRepo(const std::string& urlOrName);
|
||||
|
||||
@@ -64,7 +62,7 @@ class CPluginManager {
|
||||
|
||||
bool m_bVerbose = false;
|
||||
bool m_bNoShallow = false;
|
||||
std::string m_szCustomHlUrl, m_szUsername;
|
||||
std::string m_szCustomHlUrl;
|
||||
|
||||
// will delete recursively if exists!!
|
||||
bool createSafeDirectory(const std::string& path);
|
||||
|
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace Debug {
|
||||
template <typename... Args>
|
||||
void die(std::format_string<Args...> fmt, Args&&... args) {
|
||||
const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::cout << "\n[ERR] " << logMsg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
};
|
@@ -1,168 +0,0 @@
|
||||
#include "Sys.hpp"
|
||||
#include "Die.hpp"
|
||||
#include "StringUtils.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
inline constexpr std::array<std::string_view, 3> 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) {
|
||||
const char* PATHENV = std::getenv("PATH");
|
||||
if (!PATHENV)
|
||||
return false;
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NSys {
|
||||
bool isSuperuser();
|
||||
int getUID();
|
||||
int getEUID();
|
||||
|
||||
// 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);
|
||||
};
|
||||
};
|
@@ -2,36 +2,34 @@
|
||||
#include "helpers/StringUtils.hpp"
|
||||
#include "core/PluginManager.hpp"
|
||||
#include "core/DataState.hpp"
|
||||
#include "helpers/Sys.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <print>
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||
┃
|
||||
┣ add [url] [git rev] → Install a new plugin repository from git. Git revision
|
||||
┃ is optional, when set, commit locks are ignored.
|
||||
┣ 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.
|
||||
┣ 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.
|
||||
┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers.
|
||||
┣ list → List all installed plugins
|
||||
┃
|
||||
┣ Flags:
|
||||
┃
|
||||
┣ --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.
|
||||
┣ --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
|
||||
┗
|
||||
)#";
|
||||
|
||||
@@ -98,40 +96,21 @@ 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.headersHashCompiled != HLVER.hash) {
|
||||
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(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();
|
||||
@@ -152,7 +131,7 @@ int main(int argc, char** argv, char** envp) {
|
||||
} 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;
|
||||
}
|
||||
@@ -162,10 +141,7 @@ int main(int argc, char** argv, char** envp) {
|
||||
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.");
|
||||
@@ -183,36 +159,25 @@ int main(int argc, char** argv, char** envp) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
if (ret != LOADSTATE_OK && notify) {
|
||||
switch (ret) {
|
||||
case LOADSTATE_FAIL:
|
||||
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
||||
case LOADSTATE_HEADERS_OUTDATED:
|
||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} 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 {
|
||||
|
@@ -1,78 +1,82 @@
|
||||
#include "CProgressBar.hpp"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <print>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <print>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../helpers/Colors.hpp"
|
||||
|
||||
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 ? static_cast<float>(m_iSteps) / m_iMaxSteps : 0.0f;
|
||||
else
|
||||
percentDone = (float)m_iSteps / (float)m_iMaxSteps;
|
||||
|
||||
const auto BARWIDTH = std::clamp(w.ws_col - static_cast<unsigned long>(m_szCurrentMessage.length()) - 2, 0UL, 50UL);
|
||||
|
||||
// draw bar
|
||||
message += std::string{" "} + Colors::GREEN;
|
||||
size_t i = 0;
|
||||
for (; i < std::floor(percentDone * BARWIDTH); ++i) {
|
||||
message += "━";
|
||||
}
|
||||
// clamp to ensure no overflows (sanity check)
|
||||
percentDone = std::clamp(percentDone, 0.0f, 1.0f);
|
||||
|
||||
const size_t BARWIDTH = std::clamp<size_t>(w.ws_col - m_szCurrentMessage.length() - 2, 0, 50);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << ' ' << Colors::GREEN;
|
||||
|
||||
size_t filled = static_cast<size_t>(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("{}%", static_cast<int>(percentDone * 100.0)) << ' ';
|
||||
// draw progress
|
||||
if (m_fPercentage >= 0)
|
||||
message += " " + std::format("{}%", static_cast<int>(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);
|
||||
}
|
||||
|
@@ -1,30 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
project(hyprtester DESCRIPTION "Hyprland test suite")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
|
||||
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)
|
@@ -1,16 +0,0 @@
|
||||
CXXFLAGS = -shared -fPIC --no-gnu-unique -g -std=c++2b -Wno-c++11-narrowing
|
||||
INCLUDES = `pkg-config --cflags pixman-1 libdrm pangocairo libinput libudev wayland-server xkbcommon`
|
||||
LIBS = `pkg-config --libs pangocairo`
|
||||
|
||||
SRC = src/main.cpp
|
||||
TARGET = hyprtestplugin.so
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -I../.. -I../../protocols $(INCLUDES) $^ $> -o $@ $(LIBS) -O2
|
||||
|
||||
clean:
|
||||
rm -f ./$(TARGET)
|
||||
|
||||
.PHONY: all clean
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
make clean
|
||||
make all
|
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <src/plugins/PluginAPI.hpp>
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
@@ -1,62 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <src/includes.hpp>
|
||||
#include <sstream>
|
||||
#include <any>
|
||||
|
||||
#define private public
|
||||
#include <src/config/ConfigManager.hpp>
|
||||
#include <src/config/ConfigDescriptions.hpp>
|
||||
#include <src/layout/IHyprLayout.hpp>
|
||||
#include <src/managers/LayoutManager.hpp>
|
||||
#include <src/Compositor.hpp>
|
||||
#undef private
|
||||
|
||||
#include "globals.hpp"
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
static SDispatchResult test(std::string in) {
|
||||
bool success = true;
|
||||
std::string errors = "";
|
||||
|
||||
if (g_pConfigManager->m_configValueNumber != CONFIG_OPTIONS.size() + 1 /* autogenerated is special */) {
|
||||
errors += "config value number mismatches descriptions size\n";
|
||||
success = false;
|
||||
}
|
||||
|
||||
return SDispatchResult{
|
||||
.success = success,
|
||||
.error = errors,
|
||||
};
|
||||
}
|
||||
|
||||
// Trigger a snap move event for the active window
|
||||
static SDispatchResult snapMove(std::string in) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_lastWindow.lock();
|
||||
if (!PLASTWINDOW->m_isFloating)
|
||||
return {.success = false, .error = "Window must be floating"};
|
||||
|
||||
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
|
||||
Vector2D size = PLASTWINDOW->m_realSize->goal();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->performSnap(pos, size, PLASTWINDOW, MBIND_MOVE, -1, size);
|
||||
*PLASTWINDOW->m_realPosition = pos.round();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:test", ::test);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:snapmove", ::snapMove);
|
||||
|
||||
return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
;
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <format>
|
||||
#include <print>
|
||||
|
||||
namespace NLog {
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::string logMsg = "";
|
||||
|
||||
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::println("{}", logMsg);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
#include "hyprctlCompat.hpp"
|
||||
#include "shared.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <print>
|
||||
|
||||
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::vector<SInstanceData> instances() {
|
||||
std::vector<SInstanceData> result;
|
||||
|
||||
try {
|
||||
if (!std::filesystem::exists(getRuntimeDir()))
|
||||
return {};
|
||||
} catch (std::exception& e) { return {}; }
|
||||
|
||||
for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
|
||||
if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
|
||||
continue;
|
||||
|
||||
// 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::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string getFromSocket(const std::string& cmd) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
std::println("socket: Couldn't open a socket (1)");
|
||||
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, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
std::println("Couldn't connect to {}. (3)", socketPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("Couldn't write (4)");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string reply = "";
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
std::println("Hyprland IPC didn't respond in time");
|
||||
std::println("Couldn't read (5)");
|
||||
return "";
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == 8192) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
if (sizeWritten < 0) {
|
||||
std::println("Couldn't read (5)");
|
||||
return "";
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
|
||||
close(SERVERSOCKET);
|
||||
|
||||
return reply;
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
struct SInstanceData {
|
||||
std::string id;
|
||||
uint64_t time;
|
||||
uint64_t pid;
|
||||
std::string wlSocket;
|
||||
bool valid = true;
|
||||
};
|
||||
|
||||
std::vector<SInstanceData> instances();
|
||||
std::string getFromSocket(const std::string& cmd);
|
@@ -1,240 +0,0 @@
|
||||
|
||||
// This is a tester for Hyprland. It will launch the built binary in ./build/Hyprland
|
||||
// in headless mode and test various things.
|
||||
// for now it's quite basic and limited, but will be expanded in the future.
|
||||
|
||||
// NOTE: This tester has to be ran from its directory!!
|
||||
|
||||
// Some TODO:
|
||||
// - Add a plugin built alongside so that we can do more detailed tests (e.g. simulating keystrokes)
|
||||
// - test coverage
|
||||
// - maybe figure out a way to do some visual tests too?
|
||||
|
||||
// Required runtime deps for checks:
|
||||
// - kitty
|
||||
// - xeyes
|
||||
|
||||
#include "shared.hpp"
|
||||
#include "hyprctlCompat.hpp"
|
||||
#include "tests/main/tests.hpp"
|
||||
#include "tests/plugin/plugin.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <print>
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
|
||||
static int ret = 0;
|
||||
static SP<CProcess> hyprlandProc;
|
||||
static const std::string cwd = std::filesystem::current_path().string();
|
||||
|
||||
//
|
||||
static bool launchHyprland(std::string configPath, std::string binaryPath) {
|
||||
if (binaryPath == "") {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(cwd + "/../build/Hyprland", ec) || ec) {
|
||||
NLog::log("{}No Hyprland binary", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
binaryPath = cwd + "/../build/Hyprland";
|
||||
}
|
||||
|
||||
if (configPath == "") {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(cwd + "/test.conf", ec) || ec) {
|
||||
NLog::log("{}No test config", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
configPath = cwd + "/test.conf";
|
||||
}
|
||||
|
||||
NLog::log("{}Launching Hyprland", Colors::YELLOW);
|
||||
hyprlandProc = makeShared<CProcess>(binaryPath, std::vector<std::string>{"--config", configPath});
|
||||
hyprlandProc->addEnv("HYPRLAND_HEADLESS_ONLY", "1");
|
||||
|
||||
NLog::log("{}Launched async process", Colors::YELLOW);
|
||||
|
||||
return hyprlandProc->runAsync();
|
||||
}
|
||||
|
||||
static bool hyprlandAlive() {
|
||||
NLog::log("{}hyprlandAlive", Colors::YELLOW);
|
||||
kill(hyprlandProc->pid(), 0);
|
||||
return errno != ESRCH;
|
||||
}
|
||||
|
||||
static void help() {
|
||||
NLog::log("usage: hyprtester [arg [...]].\n");
|
||||
NLog::log(R"(Arguments:
|
||||
--help -h - Show this message again
|
||||
--config FILE -c FILE - Specify config file to use
|
||||
--binary FILE -b FILE - Specify Hyprland binary to use
|
||||
--plugin FILE -p FILE - Specify the location of the test plugin)");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
||||
std::string configPath = "";
|
||||
std::string binaryPath = "";
|
||||
std::string pluginPath = std::filesystem::current_path().string();
|
||||
|
||||
std::vector<std::string> args{argv + 1, argv + argc};
|
||||
|
||||
for (auto it = args.begin(); it != args.end(); it++) {
|
||||
if (*it == "--config" || *it == "-c") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
configPath = *std::next(it);
|
||||
|
||||
try {
|
||||
configPath = std::filesystem::canonical(configPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(configPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] Config file '{}' doesn't exist!", configPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (*it == "--binary" || *it == "-b") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
binaryPath = *std::next(it);
|
||||
|
||||
try {
|
||||
binaryPath = std::filesystem::canonical(binaryPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(binaryPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] Binary '{}' doesn't exist!", binaryPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (*it == "--plugin" || *it == "-p") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
pluginPath = *std::next(it);
|
||||
|
||||
try {
|
||||
pluginPath = std::filesystem::canonical(pluginPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(pluginPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] plugin '{}' doesn't exist!", pluginPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (*it == "--help" || *it == "-h") {
|
||||
help();
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
NLog::log("{}launching hl", Colors::YELLOW);
|
||||
if (!launchHyprland(configPath, binaryPath)) {
|
||||
NLog::log("{}well it failed", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// hyprland has launched, let's check if it's alive after 10s
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
|
||||
NLog::log("{}slept for 10s", Colors::YELLOW);
|
||||
if (!hyprlandAlive()) {
|
||||
NLog::log("{}Hyprland failed to launch", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// wonderful, we are in. Let's get the instance signature.
|
||||
NLog::log("{}trying to get INSTANCES", Colors::YELLOW);
|
||||
const auto INSTANCES = instances();
|
||||
if (INSTANCES.empty()) {
|
||||
NLog::log("{}Hyprland failed to launch (2)", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HIS = INSTANCES.back().id;
|
||||
WLDISPLAY = INSTANCES.back().wlSocket;
|
||||
|
||||
NLog::log("{}trying to get create headless output", Colors::YELLOW);
|
||||
getFromSocket("/output create headless");
|
||||
|
||||
NLog::log("{}trying to load plugin", Colors::YELLOW);
|
||||
if (const auto R = getFromSocket(std::format("/plugin load {}", pluginPath)); R != "ok") {
|
||||
NLog::log("{}Failed to load the test plugin: {}", Colors::RED, R);
|
||||
getFromSocket("/dispatch exit 1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
NLog::log("{}Loaded plugin", Colors::YELLOW);
|
||||
|
||||
for (const auto& fn : testFns) {
|
||||
EXPECT(fn(), true);
|
||||
}
|
||||
|
||||
NLog::log("{}running plugin test", Colors::YELLOW);
|
||||
EXPECT(testPlugin(), true);
|
||||
|
||||
// kill hyprland
|
||||
NLog::log("{}dispatching exit", Colors::YELLOW);
|
||||
getFromSocket("/dispatch exit");
|
||||
|
||||
NLog::log("\n{}Summary:\n\tPASSED: {}{}{}/{}\n\tFAILED: {}{}{}/{}\n{}", Colors::RESET, Colors::GREEN, TESTS_PASSED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, Colors::RED,
|
||||
TESTS_FAILED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, (TESTS_FAILED > 0 ? std::string{Colors::RED} + "\nSome tests failed.\n" : ""));
|
||||
|
||||
kill(hyprlandProc->pid(), SIGKILL);
|
||||
|
||||
hyprlandProc.reset();
|
||||
|
||||
return ret || TESTS_FAILED;
|
||||
}
|
@@ -1,90 +0,0 @@
|
||||
// Stolen from hyprutils
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
inline std::string HIS = "";
|
||||
inline std::string WLDISPLAY = "";
|
||||
inline int TESTS_PASSED = 0;
|
||||
inline int TESTS_FAILED = 0;
|
||||
|
||||
namespace Colors {
|
||||
constexpr const char* RED = "\x1b[31m";
|
||||
constexpr const char* GREEN = "\x1b[32m";
|
||||
constexpr const char* YELLOW = "\x1b[33m";
|
||||
constexpr const char* BLUE = "\x1b[34m";
|
||||
constexpr const char* MAGENTA = "\x1b[35m";
|
||||
constexpr const char* CYAN = "\x1b[36m";
|
||||
constexpr const char* RESET = "\x1b[0m";
|
||||
};
|
||||
|
||||
#define EXPECT(expr, val) \
|
||||
if (const auto RESULT = expr; RESULT != (val)) { \
|
||||
NLog::log("{}Failed: {}{}, expected {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_VECTOR2D(expr, val) \
|
||||
do { \
|
||||
const auto& RESULT = expr; \
|
||||
const auto& EXPECTED = val; \
|
||||
if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \
|
||||
NLog::log("{}Failed: {}{}, expected [{}, {}], got [{}, {}]. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, EXPECTED.x, EXPECTED.y, RESULT.x, RESULT.y, __FILE__, \
|
||||
__LINE__); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{}. Got [{}, {}].", Colors::GREEN, Colors::RESET, #expr, RESULT.x, RESULT.y); \
|
||||
TESTS_PASSED++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_CONTAINS(haystack, needle) \
|
||||
if (const auto EXPECTED = needle; !std::string{haystack}.contains(EXPECTED)) { \
|
||||
NLog::log("{}Failed: {}{} should contain {} but doesn't. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, EXPECTED, __FILE__, __LINE__, \
|
||||
std::string{haystack}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} contains {}.", Colors::GREEN, Colors::RESET, #haystack, EXPECTED); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_NOT_CONTAINS(haystack, needle) \
|
||||
if (std::string{haystack}.contains(needle)) { \
|
||||
NLog::log("{}Failed: {}{} shouldn't contain {} but does. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, #needle, __FILE__, __LINE__, \
|
||||
std::string{haystack}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} doesn't contain {}.", Colors::GREEN, Colors::RESET, #haystack, #needle); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_STARTS_WITH(str, what) \
|
||||
if (!std::string{str}.starts_with(what)) { \
|
||||
NLog::log("{}Failed: {}{} should start with {} but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, __FILE__, __LINE__, \
|
||||
std::string{str}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} starts with {}.", Colors::GREEN, Colors::RESET, #str, #what); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_COUNT_STRING(str, what, no) \
|
||||
if (Tests::countOccurrences(str, what) != no) { \
|
||||
NLog::log("{}Failed: {}{} should contain {} {} times, but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, no, __FILE__, __LINE__, \
|
||||
std::string{str}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} contains {} {} times.", Colors::GREEN, Colors::RESET, #str, #what, no); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define OK(x) EXPECT(x, "ok")
|
@@ -1,179 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing groups", Colors::GREEN);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Dispatching workspace `groups`", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace name:groups");
|
||||
|
||||
NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 1 window", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
// check kitty properties. One kitty should take the entire screen, minus the gaps.
|
||||
NLog::log("{}Check kitty dimensions", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
||||
EXPECT_COUNT_STRING(str, "size: 1876,1036", 1);
|
||||
EXPECT_COUNT_STRING(str, "fullscreen: 0", 1);
|
||||
}
|
||||
|
||||
// group the kitty
|
||||
NLog::log("{}Enable group and groupbar", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch togglegroup"));
|
||||
OK(getFromSocket("/keyword group:groupbar:enabled 1"));
|
||||
|
||||
// check the height of the window now
|
||||
NLog::log("{}Recheck kitty dimensions", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "at: 22,43");
|
||||
EXPECT_CONTAINS(str, "size: 1876,1015");
|
||||
}
|
||||
|
||||
// disable the groupbar for ease of testing for now
|
||||
NLog::log("{}Disable groupbar", Colors::YELLOW);
|
||||
OK(getFromSocket("r/keyword group:groupbar:enabled 0"));
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Kill windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Spawn kitty again", Colors::YELLOW);
|
||||
kittyProcA = Tests::spawnKitty();
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Group kitty", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch togglegroup"));
|
||||
|
||||
// check the height of the window now
|
||||
NLog::log("{}Check kitty dimensions 2", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "at: 22,22");
|
||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||
}
|
||||
|
||||
NLog::log("{}Spawn kittyProcB", Colors::YELLOW);
|
||||
auto kittyProcB = Tests::spawnKitty();
|
||||
if (!kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 2 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 2);
|
||||
|
||||
size_t lastActiveKittyIdx = 0;
|
||||
|
||||
NLog::log("{}Get last active kitty id", Colors::YELLOW);
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
lastActiveKittyIdx = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
// test cycling through
|
||||
|
||||
NLog::log("{}Test cycling through grouped windows", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch changegroupactive f"));
|
||||
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT(lastActiveKittyIdx != std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16), true);
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
getFromSocket("/dispatch changegroupactive f");
|
||||
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT(lastActiveKittyIdx, std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16));
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword group:auto_group false"));
|
||||
|
||||
NLog::log("{}Spawn kittyProcC", Colors::YELLOW);
|
||||
auto kittyProcC = Tests::spawnKitty();
|
||||
if (!kittyProcC) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 3 windows 2", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_COUNT_STRING(str, "at: 22,22", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus l"));
|
||||
OK(getFromSocket("/dispatch changegroupactive 1"));
|
||||
OK(getFromSocket("/keyword group:auto_group true"));
|
||||
OK(getFromSocket("/keyword group:insert_after_current false"));
|
||||
|
||||
NLog::log("{}Spawn kittyProcD", Colors::YELLOW);
|
||||
auto kittyProcD = Tests::spawnKitty();
|
||||
if (!kittyProcD) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 4 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 4);
|
||||
|
||||
OK(getFromSocket("/dispatch changegroupactive 3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, std::format("pid: {}", kittyProcD->pid()));
|
||||
}
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Kill windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,144 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing config: misc:", Colors::GREEN);
|
||||
|
||||
NLog::log("{}Testing close_special_on_empty", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:close_special_on_empty false"));
|
||||
OK(getFromSocket("/dispatch workspace special:test"));
|
||||
|
||||
Tests::spawnKitty();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
Tests::spawnKitty();
|
||||
|
||||
OK(getFromSocket("/keyword misc:close_special_on_empty true"));
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_NOT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
Tests::spawnKitty("kitty_B");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 1"));
|
||||
|
||||
Tests::spawnKitty("kitty_C");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 2"));
|
||||
|
||||
Tests::spawnKitty("kitty_D");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
EXPECT_CONTAINS(str, "kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Testing exit_window_retains_fullscreen", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:exit_window_retains_fullscreen false"));
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
Tests::spawnKitty("kitty_B");
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
Tests::spawnKitty("kitty_B");
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
OK(getFromSocket("/keyword misc:exit_window_retains_fullscreen true"));
|
||||
|
||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test);
|
@@ -1,174 +0,0 @@
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
using Hyprutils::Math::Vector2D;
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool spawnFloatingKitty() {
|
||||
if (!Tests::spawnKitty()) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
OK(getFromSocket("/dispatch setfloating active"));
|
||||
OK(getFromSocket("/dispatch resizeactive exact 100 100"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void expectSocket(const std::string& CMD) {
|
||||
if (const auto RESULT = getFromSocket(CMD); RESULT != "ok") {
|
||||
NLog::log("{}Failed: {}getFromSocket({}), expected ok, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, CMD, RESULT, __FILE__, __LINE__);
|
||||
ret = 1;
|
||||
TESTS_FAILED++;
|
||||
} else {
|
||||
NLog::log("{}Passed: {}getFromSocket({}). Got ok", Colors::GREEN, Colors::RESET, CMD);
|
||||
TESTS_PASSED++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expectSnapMove(const Vector2D FROM, const Vector2D* TO) {
|
||||
const Vector2D& A = FROM;
|
||||
const Vector2D& B = TO ? *TO : FROM;
|
||||
if (TO)
|
||||
NLog::log("{}Expecting snap to ({},{}) when window is moved to ({},{})", Colors::YELLOW, B.x, B.y, A.x, A.y);
|
||||
else
|
||||
NLog::log("{}Expecting no snap when window is moved to ({},{})", Colors::YELLOW, A.x, A.y);
|
||||
|
||||
expectSocket(std::format("/dispatch moveactive exact {} {}", A.x, A.y));
|
||||
expectSocket("/dispatch plugin:test:snapmove");
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("at: {},{}", B.x, B.y));
|
||||
}
|
||||
|
||||
static void testSnap(const bool OVERLAP, const bool RESPECT) {
|
||||
const double BORDERSIZE = 2;
|
||||
const double WINDOWSIZE = 100;
|
||||
|
||||
// test window snapping
|
||||
{
|
||||
const double OTHER = 500;
|
||||
const double WINDOWGAP = 8;
|
||||
const double GAPSIN = 5;
|
||||
const double GAP = (RESPECT ? GAPSIN : 0) + BORDERSIZE + (OVERLAP ? 0 : BORDERSIZE);
|
||||
const double END = GAP + WINDOWSIZE;
|
||||
|
||||
double x;
|
||||
Vector2D predict;
|
||||
|
||||
x = WINDOWGAP + END;
|
||||
expectSnapMove({OTHER + x, OTHER}, nullptr);
|
||||
expectSnapMove({OTHER - x, OTHER}, nullptr);
|
||||
expectSnapMove({OTHER, OTHER + x}, nullptr);
|
||||
expectSnapMove({OTHER, OTHER - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({OTHER + x, OTHER}, &(predict = {OTHER + END, OTHER}));
|
||||
expectSnapMove({OTHER - x, OTHER}, &(predict = {OTHER - END, OTHER}));
|
||||
expectSnapMove({OTHER, OTHER + x}, &(predict = {OTHER, OTHER + END}));
|
||||
expectSnapMove({OTHER, OTHER - x}, &(predict = {OTHER, OTHER - END}));
|
||||
}
|
||||
|
||||
// test monitor snapping
|
||||
{
|
||||
const double MONITORGAP = 10;
|
||||
const double GAPSOUT = 20;
|
||||
const double RESP = (RESPECT ? GAPSOUT : 0);
|
||||
const double GAP = RESP + (OVERLAP ? 0 : BORDERSIZE);
|
||||
const double END = GAP + WINDOWSIZE;
|
||||
|
||||
double x;
|
||||
Vector2D predict;
|
||||
|
||||
x = MONITORGAP + GAP;
|
||||
expectSnapMove({x, x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({x, x}, &(predict = {GAP, GAP}));
|
||||
|
||||
x = MONITORGAP + END;
|
||||
expectSnapMove({1920 - x, 1080 - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - END, 1080 - END}));
|
||||
|
||||
// test reserved area
|
||||
const double RESERVED = 200;
|
||||
const double RGAP = RESERVED + RESP + BORDERSIZE;
|
||||
const double REND = RGAP + WINDOWSIZE;
|
||||
|
||||
x = MONITORGAP + RGAP;
|
||||
expectSnapMove({x, x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({x, x}, &(predict = {RGAP, RGAP}));
|
||||
|
||||
x = MONITORGAP + REND;
|
||||
expectSnapMove({1920 - x, 1080 - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - REND, 1080 - REND}));
|
||||
}
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing snap", Colors::GREEN);
|
||||
|
||||
// move to monitor HEADLESS-2
|
||||
NLog::log("{}Moving to monitor HEADLESS-2", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||
NLog::log("{}Adding reserved monitor area to HEADLESS-2", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword monitor HEADLESS-2,addreserved,200,200,200,200"));
|
||||
|
||||
// test on workspace "snap"
|
||||
NLog::log("{}Dispatching workspace `snap`", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace name:snap"));
|
||||
|
||||
// spawn a kitty terminal and move to (500,500)
|
||||
NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
if (!spawnFloatingKitty())
|
||||
return false;
|
||||
|
||||
NLog::log("{}Expecting 1 window", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
NLog::log("{}Move the kitty window to (500,500)", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch moveactive exact 500 500"));
|
||||
|
||||
// spawn a second kitty terminal
|
||||
NLog::log("{}Spawning kittyProcB", Colors::YELLOW);
|
||||
if (!spawnFloatingKitty())
|
||||
return false;
|
||||
|
||||
NLog::log("{}Expecting 2 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 2);
|
||||
|
||||
NLog::log("");
|
||||
testSnap(false, false);
|
||||
|
||||
NLog::log("\n{}Turning on border_overlap", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:border_overlap true"));
|
||||
testSnap(true, false);
|
||||
|
||||
NLog::log("\n{}Turning on respect_gaps", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:border_overlap false"));
|
||||
OK(getFromSocket("/keyword general:snap:respect_gaps true"));
|
||||
testSnap(false, true);
|
||||
|
||||
NLog::log("\n{}Turning on both border_overlap and respect_gaps", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:border_overlap true"));
|
||||
testSnap(true, true);
|
||||
|
||||
// kill all
|
||||
NLog::log("\n{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
NLog::log("{}Reloading the config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
inline std::vector<std::function<bool()>> testFns;
|
||||
|
||||
#define REGISTER_TEST_FN(fn) \
|
||||
static auto _register_fn = [] { \
|
||||
testFns.emplace_back(fn); \
|
||||
return 1; \
|
||||
}();
|
@@ -1,98 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing windows", Colors::GREEN);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace `window`", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace name:window");
|
||||
|
||||
NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 1 window", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
// check kitty properties. One kitty should take the entire screen, as this is smart gaps
|
||||
NLog::log("{}Expecting kitty to take up the whole screen", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT(str.contains("at: 0,0"), true);
|
||||
EXPECT(str.contains("size: 1920,1080"), true);
|
||||
EXPECT(str.contains("fullscreen: 0"), true);
|
||||
}
|
||||
|
||||
NLog::log("{}Spawning kittyProcB", Colors::YELLOW);
|
||||
auto kittyProcB = Tests::spawnKitty();
|
||||
if (!kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 2 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 2);
|
||||
|
||||
// open xeyes
|
||||
NLog::log("{}Spawning xeyes", Colors::YELLOW);
|
||||
getFromSocket("/dispatch exec xeyes");
|
||||
|
||||
NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != 3) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
return !ret;
|
||||
}
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 3 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
|
||||
NLog::log("{}Checking props of xeyes", Colors::YELLOW);
|
||||
// check some window props of xeyes, try to tile them
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
getFromSocket("/dispatch settiled class:XEyes");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
str = getFromSocket("/clients");
|
||||
EXPECT_NOT_CONTAINS(str, "floating: 1");
|
||||
}
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,351 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing workspaces", Colors::GREEN);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Spawning kittyProc on ws 1", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 3", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 3"));
|
||||
|
||||
NLog::log("{}Spawning kittyProc on ws 3", Colors::YELLOW);
|
||||
auto kittyProcB = Tests::spawnKitty();
|
||||
|
||||
if (!kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Switching to workspace +1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace +1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
// check if the other workspaces are alive
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "workspace ID 3 (3)");
|
||||
EXPECT_CONTAINS(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Switching to workspace m+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace m+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace -1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace -1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r~1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r~1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace empty", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace empty"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace previous", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace name:TEST_WORKSPACE_NULL", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace name:TEST_WORKSPACE_NULL"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID -1337 (TEST_WORKSPACE_NULL)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
// add a new monitor
|
||||
NLog::log("{}Adding a new monitor", Colors::YELLOW);
|
||||
EXPECT(getFromSocket("/output create headless"), "ok")
|
||||
|
||||
// should take workspace 2
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "active workspace: 2 (2)");
|
||||
EXPECT_CONTAINS(str, "active workspace: 1 (1)");
|
||||
EXPECT_CONTAINS(str, "HEADLESS-3");
|
||||
}
|
||||
|
||||
// focus the first monitor
|
||||
OK(getFromSocket("/dispatch focusmonitor 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r~2", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/dispatch workspace r~2"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace m+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace m+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
// no OK: this will throw an error as it should
|
||||
getFromSocket("/dispatch workspace 1");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing back_and_forth", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:workspace_back_and_forth true"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:workspace_back_and_forth false"));
|
||||
|
||||
NLog::log("{}Testing hide_special_on_workspace_change", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:hide_special_on_workspace_change true"));
|
||||
OK(getFromSocket("/dispatch workspace special:HELLO"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
EXPECT_CONTAINS(str, "special:HELLO");
|
||||
}
|
||||
|
||||
// no OK: will err (it shouldnt prolly but oh well)
|
||||
getFromSocket("/dispatch workspace 3");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_COUNT_STRING(str, "special workspace: 0 ()", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:hide_special_on_workspace_change false"));
|
||||
|
||||
NLog::log("{}Testing allow_workspace_cycles", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:allow_workspace_cycles true"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/dispatch workspace 3"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:allow_workspace_cycles false"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
// spawn 3 kitties
|
||||
NLog::log("{}Testing focus_preferred_method", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword dwindle:force_split 2"));
|
||||
Tests::spawnKitty("kitty_A");
|
||||
Tests::spawnKitty("kitty_B");
|
||||
Tests::spawnKitty("kitty_C");
|
||||
OK(getFromSocket("/keyword dwindle:force_split 0"));
|
||||
|
||||
// focus kitty 2: will be top right (dwindle)
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
|
||||
// resize it to be a bit taller
|
||||
OK(getFromSocket("/dispatch resizeactive +20 +20"));
|
||||
|
||||
// now we test focus methods.
|
||||
OK(getFromSocket("/keyword binds:focus_preferred_method 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_C"));
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
|
||||
OK(getFromSocket("/keyword binds:focus_preferred_method 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_B");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing movefocus_cycles_fullscreen", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
||||
Tests::spawnKitty("kitty_D");
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor l"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:movefocus_cycles_fullscreen false"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor l"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:movefocus_cycles_fullscreen true"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_B");
|
||||
}
|
||||
|
||||
// destroy the headless output
|
||||
OK(getFromSocket("/output remove HEADLESS-3"));
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,21 +0,0 @@
|
||||
#include "plugin.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
bool testPlugin() {
|
||||
const auto RESPONSE = getFromSocket("/dispatch plugin:test:test");
|
||||
|
||||
if (RESPONSE != "ok") {
|
||||
NLog::log("{}Plugin tests failed, plugin returned:\n{}{}", Colors::RED, Colors::RESET, RESPONSE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
bool testPlugin();
|
@@ -1,92 +0,0 @@
|
||||
#include "shared.hpp"
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <thread>
|
||||
#include <print>
|
||||
#include "../shared.hpp"
|
||||
#include "../hyprctlCompat.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_) {
|
||||
const auto COUNT_BEFORE = windowCount();
|
||||
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", class_.empty() ? std::vector<std::string>{} : std::vector<std::string>{"--class", class_});
|
||||
kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||
kitty->runAsync();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// wait while kitty spawns
|
||||
int counter = 0;
|
||||
while (processAlive(kitty->pid()) && windowCount() == COUNT_BEFORE) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!processAlive(kitty->pid()))
|
||||
return nullptr;
|
||||
|
||||
return kitty;
|
||||
}
|
||||
|
||||
bool Tests::processAlive(pid_t pid) {
|
||||
errno = 0;
|
||||
int ret = kill(pid, 0);
|
||||
return ret != -1 || errno != ESRCH;
|
||||
}
|
||||
|
||||
int Tests::windowCount() {
|
||||
return countOccurrences(getFromSocket("/clients"), "focusHistoryID: ");
|
||||
}
|
||||
|
||||
int Tests::countOccurrences(const std::string& in, const std::string& what) {
|
||||
int cnt = 0;
|
||||
auto pos = in.find(what);
|
||||
while (pos != std::string::npos) {
|
||||
cnt++;
|
||||
pos = in.find(what, pos + what.length() - 1);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool Tests::killAllWindows() {
|
||||
auto str = getFromSocket("/clients");
|
||||
auto pos = str.find("Window ");
|
||||
while (pos != std::string::npos) {
|
||||
auto pos2 = str.find(" -> ", pos);
|
||||
getFromSocket("/dispatch killwindow address:0x" + str.substr(pos + 7, pos2 - pos - 7));
|
||||
pos = str.find("Window ", pos + 5);
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != 0) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
std::println("{}Timed out waiting for windows to close", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tests::waitUntilWindowsN(int n) {
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != n) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
std::println("{}Timed out waiting for windows", Colors::RED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../Log.hpp"
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
namespace Tests {
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "");
|
||||
bool processAlive(pid_t pid);
|
||||
int windowCount();
|
||||
int countOccurrences(const std::string& in, const std::string& what);
|
||||
bool killAllWindows();
|
||||
void waitUntilWindowsN(int n);
|
||||
};
|
@@ -1,313 +0,0 @@
|
||||
# This is an example Hyprland config file.
|
||||
# Refer to the wiki for more information.
|
||||
# https://wiki.hyprland.org/Configuring/
|
||||
|
||||
# Please note not all available settings / options are set here.
|
||||
# For a full list, see the wiki
|
||||
|
||||
# You can split this configuration into multiple files
|
||||
# Create your files separately and then link them to this file like this:
|
||||
# source = ~/.config/hypr/myColors.conf
|
||||
|
||||
|
||||
################
|
||||
### MONITORS ###
|
||||
################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Monitors/
|
||||
|
||||
monitor=HEADLESS-1,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-2,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-3,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-4,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-5,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-6,1920x1080@60,auto-right,1
|
||||
|
||||
monitor=,disabled
|
||||
|
||||
|
||||
###################
|
||||
### MY PROGRAMS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
|
||||
# Set programs that you use
|
||||
$terminal = kitty
|
||||
$fileManager = dolphin
|
||||
$menu = wofi --show drun
|
||||
|
||||
|
||||
#################
|
||||
### AUTOSTART ###
|
||||
#################
|
||||
|
||||
# Autostart necessary processes (like notifications daemons, status bars, etc.)
|
||||
# Or execute your favorite apps at launch like this:
|
||||
|
||||
# exec-once = $terminal
|
||||
# exec-once = nm-applet &
|
||||
# exec-once = waybar & hyprpaper & firefox
|
||||
|
||||
|
||||
#############################
|
||||
### ENVIRONMENT VARIABLES ###
|
||||
#############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
|
||||
# Refer to https://wiki.hyprland.org/Configuring/Variables/
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 20
|
||||
|
||||
border_size = 2
|
||||
|
||||
snap {
|
||||
enabled = true
|
||||
window_gap = 8
|
||||
monitor_gap = 10
|
||||
respect_gaps = false
|
||||
border_overlap = false
|
||||
}
|
||||
|
||||
# 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.hyprland.org/Configuring/Tearing/ before you turn this on
|
||||
allow_tearing = false
|
||||
|
||||
layout = dwindle
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
inactive_opacity = 1.0
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
range = 4
|
||||
render_power = 3
|
||||
color = rgba(1a1a1aee)
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#blur
|
||||
blur {
|
||||
enabled = true
|
||||
size = 3
|
||||
passes = 1
|
||||
|
||||
vibrancy = 0.1696
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = 0
|
||||
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
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.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
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# 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.hyprland.org/Configuring/Master-Layout/ for more
|
||||
master {
|
||||
new_status = master
|
||||
}
|
||||
|
||||
# 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. :(
|
||||
}
|
||||
|
||||
|
||||
#############
|
||||
### INPUT ###
|
||||
#############
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_variant =
|
||||
kb_model =
|
||||
kb_options =
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 1
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
touchpad {
|
||||
natural_scroll = false
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#gestures
|
||||
gestures {
|
||||
workspace_swipe = false
|
||||
}
|
||||
|
||||
# Example per-device config
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
|
||||
device {
|
||||
name = epic-mouse-v1
|
||||
sensitivity = -0.5
|
||||
}
|
||||
|
||||
|
||||
###################
|
||||
### KEYBINDINGS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
$mainMod = SUPER # Sets "Windows" key as main modifier
|
||||
|
||||
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
|
||||
bind = $mainMod, Q, exec, $terminal
|
||||
bind = $mainMod, C, killactive,
|
||||
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, togglesplit, # dwindle
|
||||
|
||||
# Move focus with mainMod + arrow keys
|
||||
bind = $mainMod, left, movefocus, l
|
||||
bind = $mainMod, right, movefocus, r
|
||||
bind = $mainMod, up, movefocus, u
|
||||
bind = $mainMod, down, movefocus, d
|
||||
|
||||
# Switch workspaces with mainMod + [0-9]
|
||||
bind = $mainMod, 1, workspace, 1
|
||||
bind = $mainMod, 2, workspace, 2
|
||||
bind = $mainMod, 3, workspace, 3
|
||||
bind = $mainMod, 4, workspace, 4
|
||||
bind = $mainMod, 5, workspace, 5
|
||||
bind = $mainMod, 6, workspace, 6
|
||||
bind = $mainMod, 7, workspace, 7
|
||||
bind = $mainMod, 8, workspace, 8
|
||||
bind = $mainMod, 9, workspace, 9
|
||||
bind = $mainMod, 0, workspace, 10
|
||||
|
||||
# Move active window to a workspace with mainMod + SHIFT + [0-9]
|
||||
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||
|
||||
# Example special workspace (scratchpad)
|
||||
bind = $mainMod, S, togglespecialworkspace, magic
|
||||
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
|
||||
|
||||
# Scroll through existing workspaces with mainMod + scroll
|
||||
bind = $mainMod, mouse_down, workspace, e+1
|
||||
bind = $mainMod, mouse_up, workspace, e-1
|
||||
|
||||
# Move/resize windows with mainMod + LMB/RMB and dragging
|
||||
bindm = $mainMod, mouse:272, movewindow
|
||||
bindm = $mainMod, mouse:273, resizewindow
|
||||
|
||||
# Laptop multimedia keys for volume and LCD brightness
|
||||
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
bindl = , XF86AudioPause, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPlay, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
|
||||
##############################
|
||||
### WINDOWS AND WORKSPACES ###
|
||||
##############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
|
||||
# Workspace "windows" is a smart gaps one
|
||||
workspace = n[s:window] w[tv1], gapsout:0, gapsin:0
|
||||
workspace = n[s:window] f[1], gapsout:0, gapsin:0
|
||||
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] w[tv1]
|
||||
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] w[tv1]
|
||||
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] f[1]
|
||||
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] f[1]
|
14
meson.build
14
meson.build
@@ -31,11 +31,11 @@ if cpp_compiler.check_header('execinfo.h')
|
||||
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
|
||||
endif
|
||||
|
||||
aquamarine = dependency('aquamarine', version: '>=0.9.0')
|
||||
aquamarine = dependency('aquamarine', version: '>=0.4.5')
|
||||
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.3')
|
||||
hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1')
|
||||
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
||||
hyprutils = dependency('hyprutils', version: '>= 0.8.1')
|
||||
hyprutils = dependency('hyprutils', version: '>= 0.2.3')
|
||||
aquamarine_version_list = aquamarine.version().split('.')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
|
||||
@@ -77,17 +77,19 @@ if (systemd_option.enabled())
|
||||
subdir('systemd')
|
||||
endif
|
||||
|
||||
if get_option('legacy_renderer').enabled()
|
||||
add_project_arguments('-DLEGACY_RENDERER', language: 'cpp')
|
||||
endif
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
|
||||
endif
|
||||
|
||||
# Generate hyprland version and populate version.h
|
||||
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
|
||||
# Make shader files includable
|
||||
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
|
||||
|
||||
# Install headers
|
||||
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.inc', check: true)
|
||||
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.frag', check: true)
|
||||
headers = globber.stdout().strip().split('\n')
|
||||
foreach file : headers
|
||||
install_headers(file, subdir: 'hyprland', preserve_path: true)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
|
||||
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
|
||||
option('uwsm', type: 'feature', value: 'enabled', description: 'Enable uwsm integration (only if systemd is enabled)')
|
||||
option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')
|
||||
option('hyprpm', type: 'feature', value: 'enabled', description: 'Enable hyprpm')
|
||||
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')
|
||||
|
@@ -11,7 +11,6 @@
|
||||
aquamarine,
|
||||
binutils,
|
||||
cairo,
|
||||
epoll-shim,
|
||||
git,
|
||||
glaze,
|
||||
hyprcursor,
|
||||
@@ -41,6 +40,7 @@
|
||||
xwayland,
|
||||
debug ? false,
|
||||
enableXWayland ? true,
|
||||
legacyRenderer ? false,
|
||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
||||
wrapRuntimeDeps ? true,
|
||||
version ? "git",
|
||||
@@ -51,14 +51,13 @@
|
||||
enableNvidiaPatches ? false,
|
||||
nvidiaPatches ? false,
|
||||
hidpiXWayland ? false,
|
||||
legacyRenderer ? false,
|
||||
}: let
|
||||
inherit (builtins) foldl' readFile;
|
||||
inherit (builtins) baseNameOf foldl' readFile;
|
||||
inherit (lib.asserts) assertMsg;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.lists) flatten concatLists optional optionals;
|
||||
inherit (lib.strings) makeBinPath optionalString mesonBool mesonEnable trim;
|
||||
fs = lib.fileset;
|
||||
inherit (lib.sources) cleanSourceWith cleanSource;
|
||||
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable trim;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
@@ -69,34 +68,17 @@
|
||||
in
|
||||
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
||||
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
||||
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
|
||||
customStdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hyprland${optionalString debug "-debug"}";
|
||||
inherit version;
|
||||
|
||||
src = fs.toSource {
|
||||
root = ../.;
|
||||
fileset =
|
||||
fs.intersection
|
||||
# allows non-flake builds to only include files tracked by git
|
||||
(fs.gitTracked ../.)
|
||||
(fs.unions [
|
||||
../assets/hyprland-portals.conf
|
||||
../assets/install
|
||||
../hyprctl
|
||||
../hyprland.pc.in
|
||||
../LICENSE
|
||||
../meson_options.txt
|
||||
../protocols
|
||||
../src
|
||||
../systemd
|
||||
../VERSION
|
||||
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
||||
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example)
|
||||
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
|
||||
(fs.fileFilter (file: file.name == "meson.build") ../.)
|
||||
]);
|
||||
src = cleanSourceWith {
|
||||
filter = name: _type: let
|
||||
baseName = baseNameOf (toString name);
|
||||
in
|
||||
! (hasSuffix ".nix" baseName);
|
||||
src = cleanSource ../.;
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
@@ -159,7 +141,6 @@ in
|
||||
wayland-scanner
|
||||
xorg.libXcursor
|
||||
]
|
||||
(optionals customStdenv.hostPlatform.isBSD [epoll-shim])
|
||||
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
|
||||
(optionals enableXWayland [
|
||||
xorg.libxcb
|
||||
@@ -172,8 +153,6 @@ in
|
||||
(optional withSystemd systemd)
|
||||
];
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
mesonBuildType =
|
||||
if debug
|
||||
then "debug"
|
||||
@@ -182,7 +161,7 @@ in
|
||||
mesonFlags = flatten [
|
||||
(mapAttrsToList mesonEnable {
|
||||
"xwayland" = enableXWayland;
|
||||
"systemd" = withSystemd;
|
||||
"legacy_renderer" = legacyRenderer;
|
||||
"uwsm" = false;
|
||||
"hyprpm" = false;
|
||||
})
|
||||
|
@@ -1,60 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
stdenvAdapters,
|
||||
cmake,
|
||||
pkg-config,
|
||||
hyprland,
|
||||
hyprwayland-scanner,
|
||||
version ? "git",
|
||||
}: let
|
||||
inherit (lib.lists) flatten foldl';
|
||||
inherit (lib.sources) cleanSourceWith cleanSource;
|
||||
inherit (lib.strings) hasSuffix cmakeBool;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
stdenvAdapters.keepDebugInfo
|
||||
];
|
||||
|
||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||
in
|
||||
customStdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hyprtester";
|
||||
inherit version;
|
||||
|
||||
src = cleanSourceWith {
|
||||
filter = name: _type: let
|
||||
baseName = baseNameOf (toString name);
|
||||
in
|
||||
! (hasSuffix ".nix" baseName);
|
||||
src = cleanSource ../.;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
hyprwayland-scanner
|
||||
];
|
||||
|
||||
buildInputs = hyprland.buildInputs;
|
||||
|
||||
preConfigure = ''
|
||||
cmake -S . -B .
|
||||
cmake --build . --target generate-protocol-headers -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
cd hyprtester
|
||||
'';
|
||||
|
||||
cmakeBuildType = "Debug";
|
||||
|
||||
cmakeFlags = [(cmakeBool "TESTS" true)];
|
||||
|
||||
meta = {
|
||||
homepage = "https://github.com/hyprwm/Hyprland";
|
||||
description = "Hyprland testing framework";
|
||||
license = lib.licenses.bsd3;
|
||||
platforms = hyprland.meta.platforms;
|
||||
mainProgram = "hyprtester";
|
||||
};
|
||||
})
|
@@ -40,7 +40,7 @@ in {
|
||||
description = ''
|
||||
Hyprland configuration written in Nix. Entries with the same key
|
||||
should be written as lists. Variables' and colors' names should be
|
||||
quoted. See <https://wiki.hypr.land> for more examples.
|
||||
quoted. See <https://wiki.hyprland.org> for more examples.
|
||||
|
||||
Special categories (e.g `devices`) should be written as
|
||||
`"devices[device-name]"`.
|
||||
@@ -126,14 +126,13 @@ in {
|
||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||
}
|
||||
{
|
||||
"exec-once" = let
|
||||
plugin = let
|
||||
mkEntry = entry:
|
||||
if lib.types.package.check entry
|
||||
then "${entry}/lib/lib${entry.pname}.so"
|
||||
else entry;
|
||||
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
||||
in
|
||||
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
||||
map mkEntry cfg.plugins;
|
||||
};
|
||||
in
|
||||
lib.mkIf shouldGenerate {
|
||||
|
@@ -8,7 +8,7 @@
|
||||
(builtins.substring 4 2 longDate)
|
||||
(builtins.substring 6 2 longDate)
|
||||
]);
|
||||
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||
version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||
in {
|
||||
# Contains what a user is most likely to care about:
|
||||
# Hyprland itself, XDPH and the Share Picker.
|
||||
@@ -33,28 +33,25 @@ in {
|
||||
# Hyprland packages themselves
|
||||
(final: _prev: let
|
||||
date = mkDate (self.lastModifiedDate or "19700101");
|
||||
version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
|
||||
in {
|
||||
hyprland = final.callPackage ./default.nix {
|
||||
stdenv = final.gcc15Stdenv;
|
||||
stdenv = final.gcc14Stdenv;
|
||||
version = "${version}+date=${date}_${self.shortRev or "dirty"}";
|
||||
commit = self.rev or "";
|
||||
revCount = self.sourceInfo.revCount or "";
|
||||
inherit date version;
|
||||
inherit date;
|
||||
};
|
||||
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
|
||||
|
||||
hyprtester = final.callPackage ./hyprtester.nix {
|
||||
inherit version;
|
||||
# Build major libs with debug to get as much info as possible in a stacktrace
|
||||
hyprland-debug = final.hyprland.override {
|
||||
aquamarine = final.aquamarine.override {debug = true;};
|
||||
hyprutils = final.hyprutils.override {debug = true;};
|
||||
debug = true;
|
||||
};
|
||||
hyprland-legacy-renderer = final.hyprland.override {legacyRenderer = true;};
|
||||
|
||||
# deprecated packages
|
||||
hyprland-legacy-renderer =
|
||||
builtins.trace ''
|
||||
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
||||
Legacy renderer is no longer supported.
|
||||
''
|
||||
final.hyprland;
|
||||
|
||||
hyprland-nvidia =
|
||||
builtins.trace ''
|
||||
hyprland-nvidia was removed. Please use the hyprland package.
|
||||
@@ -65,24 +62,12 @@ in {
|
||||
hyprland-hidpi =
|
||||
builtins.trace ''
|
||||
hyprland-hidpi was removed. Please use the hyprland package.
|
||||
For more information, refer to https://wiki.hypr.land/Configuring/XWayland.
|
||||
For more information, refer to https://wiki.hyprland.org/Configuring/XWayland.
|
||||
''
|
||||
final.hyprland;
|
||||
})
|
||||
];
|
||||
|
||||
# Debug
|
||||
hyprland-debug = lib.composeManyExtensions [
|
||||
# Dependencies
|
||||
self.overlays.hyprland-packages
|
||||
|
||||
(final: prev: {
|
||||
aquamarine = prev.aquamarine.override {debug = true;};
|
||||
hyprutils = prev.hyprutils.override {debug = true;};
|
||||
hyprland-debug = prev.hyprland.override {debug = true;};
|
||||
})
|
||||
];
|
||||
|
||||
# Packages for extra software recommended for usage with Hyprland,
|
||||
# including forked or patched packages for compatibility.
|
||||
hyprland-extras = lib.composeManyExtensions [
|
||||
|
@@ -1,86 +0,0 @@
|
||||
inputs: pkgs: let
|
||||
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
hyprland = flake.hyprland;
|
||||
in {
|
||||
tests = pkgs.testers.runNixOSTest {
|
||||
name = "hyprland-tests";
|
||||
|
||||
nodes.machine = {pkgs, ...}: {
|
||||
environment.systemPackages = with pkgs; [
|
||||
flake.hyprtester
|
||||
|
||||
# Programs needed for tests
|
||||
kitty
|
||||
xorg.xeyes
|
||||
];
|
||||
|
||||
# Enabled by default for some reason
|
||||
services.speechd.enable = false;
|
||||
|
||||
environment.variables = {
|
||||
"AQ_TRACE" = "1";
|
||||
"HYPRLAND_TRACE" = "1";
|
||||
"XDG_RUNTIME_DIR" = "/tmp";
|
||||
"XDG_CACHE_HOME" = "/tmp";
|
||||
};
|
||||
|
||||
programs.hyprland = {
|
||||
enable = true;
|
||||
package = hyprland;
|
||||
# We don't need portals in this test, so we don't set portalPackage
|
||||
};
|
||||
|
||||
# Test configuration
|
||||
environment.etc."test.conf".source = "${flake.hyprtester}/share/hypr/test.conf";
|
||||
|
||||
# Disable portals
|
||||
xdg.portal.enable = pkgs.lib.mkForce false;
|
||||
|
||||
# Autologin root into tty
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
system.stateVersion = "24.11";
|
||||
|
||||
users.users.alice = {
|
||||
isNormalUser = true;
|
||||
};
|
||||
|
||||
virtualisation = {
|
||||
cores = 4;
|
||||
# Might crash with less
|
||||
memorySize = 8192;
|
||||
resolution = {
|
||||
x = 1920;
|
||||
y = 1080;
|
||||
};
|
||||
|
||||
# Doesn't seem to do much, thought it would fix XWayland crashing
|
||||
qemu.options = ["-vga none -device virtio-gpu-pci"];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Wait for tty to be up
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
# Run hyprtester testing framework/suite
|
||||
print("Running hyprtester")
|
||||
exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${flake.hyprtester}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'")
|
||||
print(f"Hyprtester exited with {exit_status}")
|
||||
|
||||
# Copy logs to host
|
||||
machine.execute('cp "$(find /tmp/hypr -name *.log | head -1)" /tmp/hyprlog')
|
||||
machine.execute(f'echo {exit_status} > /tmp/exit_status')
|
||||
machine.copy_from_vm("/tmp/testerlog")
|
||||
machine.copy_from_vm("/tmp/hyprlog")
|
||||
machine.copy_from_vm("/tmp/exit_status")
|
||||
|
||||
# Print logs for visibility in CI
|
||||
_, out = machine.execute("cat /tmp/testerlog")
|
||||
print(f"Hyprtester log:\n{out}")
|
||||
|
||||
# Finally - shutdown
|
||||
machine.shutdown()
|
||||
'';
|
||||
};
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
wayland_protos = dependency(
|
||||
'wayland-protocols',
|
||||
version: '>=1.43',
|
||||
version: '>=1.41',
|
||||
fallback: 'wayland-protocols',
|
||||
default_options: ['tests=false'],
|
||||
)
|
||||
|
||||
hyprland_protos = dependency(
|
||||
'hyprland-protocols',
|
||||
version: '>=0.6.4',
|
||||
version: '>=0.6.2',
|
||||
fallback: 'hyprland-protocols',
|
||||
)
|
||||
|
||||
@@ -37,7 +37,6 @@ protocols = [
|
||||
'frog-color-management-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-toplevel-mapping-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
|
||||
hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml',
|
||||
@@ -73,9 +72,6 @@ protocols = [
|
||||
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
|
||||
wayland_protocol_dir / 'staging/content-type/content-type-v1.xml',
|
||||
wayland_protocol_dir / 'staging/color-management/color-management-v1.xml',
|
||||
wayland_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml',
|
||||
wayland_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml',
|
||||
wayland_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml',
|
||||
]
|
||||
|
||||
wl_protocols = []
|
||||
@@ -91,7 +87,7 @@ foreach protocol : protocols
|
||||
endforeach
|
||||
|
||||
# wayland.xml generation
|
||||
wayland_scanner = dependency('wayland-scanner', native: true)
|
||||
wayland_scanner = dependency('wayland-scanner')
|
||||
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
|
||||
|
||||
wayland_xml = wayland_scanner_datadir / 'wayland.xml'
|
||||
|
@@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
SHADERS_SRC="./src/render/shaders/glsl"
|
||||
|
||||
echo "-- Generating shader includes"
|
||||
|
||||
if [ ! -d ./src/render/shaders ]; then
|
||||
mkdir ./src/render/shaders
|
||||
fi
|
||||
|
||||
echo '#pragma once' > ./src/render/shaders/Shaders.hpp
|
||||
echo '#include <map>' >> ./src/render/shaders/Shaders.hpp
|
||||
echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/render/shaders/Shaders.hpp
|
||||
|
||||
for filename in `ls ${SHADERS_SRC}`; do
|
||||
echo "-- ${filename}"
|
||||
|
||||
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
||||
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
||||
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
||||
echo "}," >> ./src/render/shaders/Shaders.hpp
|
||||
done
|
||||
|
||||
echo '};' >> ./src/render/shaders/Shaders.hpp
|
1377
src/Compositor.cpp
1377
src/Compositor.cpp
File diff suppressed because it is too large
Load Diff
@@ -25,53 +25,53 @@ class CCompositor {
|
||||
CCompositor(bool onlyConfig = false);
|
||||
~CCompositor();
|
||||
|
||||
wl_display* m_wlDisplay = nullptr;
|
||||
wl_event_loop* m_wlEventLoop = nullptr;
|
||||
int m_drmFD = -1;
|
||||
bool m_initialized = false;
|
||||
SP<Aquamarine::CBackend> m_aqBackend;
|
||||
wl_display* m_sWLDisplay = nullptr;
|
||||
wl_event_loop* m_sWLEventLoop = nullptr;
|
||||
int m_iDRMFD = -1;
|
||||
bool m_bInitialized = false;
|
||||
SP<Aquamarine::CBackend> m_pAqBackend;
|
||||
|
||||
std::string m_hyprTempDataRoot = "";
|
||||
std::string m_szHyprTempDataRoot = "";
|
||||
|
||||
std::string m_wlDisplaySocket = "";
|
||||
std::string m_instanceSignature = "";
|
||||
std::string m_instancePath = "";
|
||||
std::string m_currentSplash = "error";
|
||||
std::string m_szWLDisplaySocket = "";
|
||||
std::string m_szInstanceSignature = "";
|
||||
std::string m_szInstancePath = "";
|
||||
std::string m_szCurrentSplash = "error";
|
||||
|
||||
std::vector<PHLMONITOR> m_monitors;
|
||||
std::vector<PHLMONITOR> m_realMonitors; // for all monitors, even those turned off
|
||||
std::vector<PHLWINDOW> m_windows;
|
||||
std::vector<PHLLS> m_layers;
|
||||
std::vector<PHLWORKSPACE> m_workspaces;
|
||||
std::vector<PHLWINDOWREF> m_windowsFadingOut;
|
||||
std::vector<PHLLSREF> m_surfacesFadingOut;
|
||||
std::vector<PHLMONITOR> m_vMonitors;
|
||||
std::vector<PHLMONITOR> m_vRealMonitors; // for all monitors, even those turned off
|
||||
std::vector<PHLWINDOW> m_vWindows;
|
||||
std::vector<PHLLS> m_vLayers;
|
||||
std::vector<PHLWORKSPACE> m_vWorkspaces;
|
||||
std::vector<PHLWINDOWREF> m_vWindowsFadingOut;
|
||||
std::vector<PHLLSREF> m_vSurfacesFadingOut;
|
||||
|
||||
std::unordered_map<std::string, MONITORID> m_monitorIDMap;
|
||||
std::unordered_map<std::string, WORKSPACEID> m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs
|
||||
std::unordered_map<std::string, MONITORID> m_mMonitorIDMap;
|
||||
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
void cleanup();
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
void cleanup();
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
|
||||
WP<CWLSurfaceResource> m_lastFocus;
|
||||
PHLWINDOWREF m_lastWindow;
|
||||
PHLMONITORREF m_lastMonitor;
|
||||
WP<CWLSurfaceResource> m_pLastFocus;
|
||||
PHLWINDOWREF m_pLastWindow;
|
||||
PHLMONITORREF m_pLastMonitor;
|
||||
|
||||
std::vector<PHLWINDOWREF> m_windowFocusHistory; // first element is the most recently focused
|
||||
std::vector<PHLWINDOWREF> m_vWindowFocusHistory; // first element is the most recently focused.
|
||||
|
||||
bool m_readyToProcess = false;
|
||||
bool m_sessionActive = true;
|
||||
bool m_dpmsStateOn = true;
|
||||
bool m_unsafeState = false; // unsafe state is when there is no monitors
|
||||
PHLMONITORREF m_unsafeOutput; // fallback output for the unsafe state
|
||||
bool m_isShuttingDown = false;
|
||||
bool m_finalRequests = false;
|
||||
bool m_desktopEnvSet = false;
|
||||
bool m_wantsXwayland = true;
|
||||
bool m_onlyConfigVerification = false;
|
||||
bool m_bReadyToProcess = false;
|
||||
bool m_bSessionActive = true;
|
||||
bool m_bDPMSStateON = true;
|
||||
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
|
||||
bool m_bNextIsUnsafe = false;
|
||||
PHLMONITORREF m_pUnsafeOutput; // fallback output for the unsafe state
|
||||
bool m_bIsShuttingDown = false;
|
||||
bool m_bFinalRequests = false;
|
||||
bool m_bDesktopEnvSet = false;
|
||||
bool m_bWantsXwayland = true;
|
||||
bool m_bOnlyConfigVerification = false;
|
||||
|
||||
// ------------------------------------------------- //
|
||||
|
||||
@@ -85,7 +85,7 @@ class CCompositor {
|
||||
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
|
||||
bool monitorExists(PHLMONITOR);
|
||||
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
|
||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
|
||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*);
|
||||
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, PHLMONITOR monitor, Vector2D*, PHLLS*);
|
||||
SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
|
||||
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
|
||||
@@ -152,7 +152,7 @@ class CCompositor {
|
||||
NColorManagement::SImageDescription getPreferredImageDescription();
|
||||
bool shouldChangePreferredImageDescription();
|
||||
|
||||
std::string m_explicitConfigPath;
|
||||
std::string explicitConfigPath;
|
||||
|
||||
private:
|
||||
void initAllSignals();
|
||||
@@ -165,9 +165,9 @@ class CCompositor {
|
||||
void removeLockFile();
|
||||
void setMallocThreshold();
|
||||
|
||||
uint64_t m_hyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_originalNofile = {};
|
||||
uint64_t m_iHyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_sOriginalNofile = {};
|
||||
};
|
||||
|
||||
inline UP<CCompositor> g_pCompositor;
|
||||
|
@@ -58,8 +58,8 @@ struct SDispatchResult {
|
||||
std::string error;
|
||||
};
|
||||
|
||||
using WINDOWID = int64_t;
|
||||
using MONITORID = int64_t;
|
||||
using WORKSPACEID = int64_t;
|
||||
typedef int64_t WINDOWID;
|
||||
typedef int64_t MONITORID;
|
||||
typedef int64_t WORKSPACEID;
|
||||
|
||||
using HOOK_CALLBACK_FN = std::function<void(void*, SCallbackInfo&, std::any)>;
|
||||
typedef std::function<void(void*, SCallbackInfo&, std::any)> HOOK_CALLBACK_FN;
|
||||
|
@@ -2,13 +2,11 @@
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/varlist/VarList.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
enum eConfigValueDataTypes : int8_t {
|
||||
CVD_TYPE_INVALID = -1,
|
||||
CVD_TYPE_GRADIENT = 0,
|
||||
CVD_TYPE_CSS_VALUE = 1,
|
||||
CVD_TYPE_FONT_WEIGHT = 2,
|
||||
CVD_TYPE_INVALID = -1,
|
||||
CVD_TYPE_GRADIENT = 0,
|
||||
CVD_TYPE_CSS_VALUE = 1
|
||||
};
|
||||
|
||||
class ICustomConfigValueData {
|
||||
@@ -24,7 +22,7 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CGradientValueData() = default;
|
||||
CGradientValueData(CHyprColor col) {
|
||||
m_colors.push_back(col);
|
||||
m_vColors.push_back(col);
|
||||
updateColorsOk();
|
||||
};
|
||||
virtual ~CGradientValueData() = default;
|
||||
@@ -34,39 +32,39 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
void reset(CHyprColor col) {
|
||||
m_colors.clear();
|
||||
m_colors.emplace_back(col);
|
||||
m_angle = 0;
|
||||
m_vColors.clear();
|
||||
m_vColors.emplace_back(col);
|
||||
m_fAngle = 0;
|
||||
updateColorsOk();
|
||||
}
|
||||
|
||||
void updateColorsOk() {
|
||||
m_colorsOkLabA.clear();
|
||||
for (auto& c : m_colors) {
|
||||
m_vColorsOkLabA.clear();
|
||||
for (auto& c : m_vColors) {
|
||||
const auto OKLAB = c.asOkLab();
|
||||
m_colorsOkLabA.emplace_back(OKLAB.l);
|
||||
m_colorsOkLabA.emplace_back(OKLAB.a);
|
||||
m_colorsOkLabA.emplace_back(OKLAB.b);
|
||||
m_colorsOkLabA.emplace_back(c.a);
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.l);
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.a);
|
||||
m_vColorsOkLabA.emplace_back(OKLAB.b);
|
||||
m_vColorsOkLabA.emplace_back(c.a);
|
||||
}
|
||||
}
|
||||
|
||||
/* Vector containing the colors */
|
||||
std::vector<CHyprColor> m_colors;
|
||||
std::vector<CHyprColor> m_vColors;
|
||||
|
||||
/* Vector containing pure colors for shoving into opengl */
|
||||
std::vector<float> m_colorsOkLabA;
|
||||
std::vector<float> m_vColorsOkLabA;
|
||||
|
||||
/* Float corresponding to the angle (rad) */
|
||||
float m_angle = 0;
|
||||
float m_fAngle = 0;
|
||||
|
||||
//
|
||||
bool operator==(const CGradientValueData& other) const {
|
||||
if (other.m_colors.size() != m_colors.size() || m_angle != other.m_angle)
|
||||
if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m_colors.size(); ++i)
|
||||
if (m_colors[i] != other.m_colors[i])
|
||||
for (size_t i = 0; i < m_vColors.size(); ++i)
|
||||
if (m_vColors[i] != other.m_vColors[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -74,28 +72,28 @@ class CGradientValueData : public ICustomConfigValueData {
|
||||
|
||||
virtual std::string toString() {
|
||||
std::string result;
|
||||
for (auto& c : m_colors) {
|
||||
for (auto& c : m_vColors) {
|
||||
result += std::format("{:x} ", c.getAsHex());
|
||||
}
|
||||
|
||||
result += std::format("{}deg", (int)(m_angle * 180.0 / M_PI));
|
||||
result += std::format("{}deg", (int)(m_fAngle * 180.0 / M_PI));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class CCssGapData : public ICustomConfigValueData {
|
||||
public:
|
||||
CCssGapData() : m_top(0), m_right(0), m_bottom(0), m_left(0) {};
|
||||
CCssGapData(int64_t global) : m_top(global), m_right(global), m_bottom(global), m_left(global) {};
|
||||
CCssGapData(int64_t vertical, int64_t horizontal) : m_top(vertical), m_right(horizontal), m_bottom(vertical), m_left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t horizontal, int64_t bottom) : m_top(top), m_right(horizontal), m_bottom(bottom), m_left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t right, int64_t bottom, int64_t left) : m_top(top), m_right(right), m_bottom(bottom), m_left(left) {};
|
||||
CCssGapData() : top(0), right(0), bottom(0), left(0) {};
|
||||
CCssGapData(int64_t global) : top(global), right(global), bottom(global), left(global) {};
|
||||
CCssGapData(int64_t vertical, int64_t horizontal) : top(vertical), right(horizontal), bottom(vertical), left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t horizontal, int64_t bottom) : top(top), right(horizontal), bottom(bottom), left(horizontal) {};
|
||||
CCssGapData(int64_t top, int64_t right, int64_t bottom, int64_t left) : top(top), right(right), bottom(bottom), left(left) {};
|
||||
|
||||
/* Css like directions */
|
||||
int64_t m_top;
|
||||
int64_t m_right;
|
||||
int64_t m_bottom;
|
||||
int64_t m_left;
|
||||
int64_t top;
|
||||
int64_t right;
|
||||
int64_t bottom;
|
||||
int64_t left;
|
||||
|
||||
void parseGapData(CVarList varlist) {
|
||||
switch (varlist.size()) {
|
||||
@@ -124,10 +122,10 @@ class CCssGapData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
void reset(int64_t global) {
|
||||
m_top = global;
|
||||
m_right = global;
|
||||
m_bottom = global;
|
||||
m_left = global;
|
||||
top = global;
|
||||
right = global;
|
||||
bottom = global;
|
||||
left = global;
|
||||
}
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
@@ -135,44 +133,6 @@ class CCssGapData : public ICustomConfigValueData {
|
||||
}
|
||||
|
||||
virtual std::string toString() {
|
||||
return std::format("{} {} {} {}", m_top, m_right, m_bottom, m_left);
|
||||
}
|
||||
};
|
||||
|
||||
class CFontWeightConfigValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CFontWeightConfigValueData() = default;
|
||||
CFontWeightConfigValueData(const char* weight) {
|
||||
parseWeight(weight);
|
||||
}
|
||||
|
||||
int64_t m_value = 400; // default to normal weight
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
return CVD_TYPE_FONT_WEIGHT;
|
||||
}
|
||||
|
||||
virtual std::string toString() {
|
||||
return std::format("{}", m_value);
|
||||
}
|
||||
|
||||
void parseWeight(const std::string& strWeight) {
|
||||
auto lcWeight{strWeight};
|
||||
std::ranges::transform(strWeight, lcWeight.begin(), ::tolower);
|
||||
|
||||
// values taken from Pango weight enums
|
||||
const auto WEIGHTS = std::map<std::string, int>{
|
||||
{"thin", 100}, {"ultralight", 200}, {"light", 300}, {"semilight", 350}, {"book", 380}, {"normal", 400},
|
||||
{"medium", 500}, {"semibold", 600}, {"bold", 700}, {"ultrabold", 800}, {"heavy", 900}, {"ultraheavy", 1000},
|
||||
};
|
||||
|
||||
auto weight = WEIGHTS.find(lcWeight);
|
||||
if (weight != WEIGHTS.end())
|
||||
m_value = weight->second;
|
||||
else {
|
||||
int w_i = std::stoi(strWeight);
|
||||
if (w_i < 100 || w_i > 1000)
|
||||
m_value = 400;
|
||||
}
|
||||
return std::format("{} {} {} {}", top, right, bottom, left);
|
||||
}
|
||||
};
|
||||
|
@@ -32,13 +32,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"20"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:float_gaps",
|
||||
.description = "gaps between windows and monitor edges for floating windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20). \n-1 means default "
|
||||
"gaps_in/gaps_out\n0 means no gaps",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"0"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:gaps_workspaces",
|
||||
.description = "gaps between workspaces. Stacks with gaps_out.",
|
||||
@@ -135,12 +128,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:snap:respect_gaps",
|
||||
.description = "if true, snapping will respect gaps between windows",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* decoration:
|
||||
@@ -260,12 +247,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_STRING_LONG,
|
||||
.data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "decoration:border_part_of_window",
|
||||
.description = "whether the border should be treated as a part of the window.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
|
||||
/*
|
||||
* blur:
|
||||
@@ -385,12 +366,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "animations:workspace_wraparound",
|
||||
.description = "changes the direction of slide animations between the first and last workspaces",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
|
||||
/*
|
||||
* input:
|
||||
@@ -623,10 +598,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:touchpad:drag_lock",
|
||||
.description = "When enabled, lifting the finger off while dragging will not drop the dragged item. 0 -> disabled, 1 -> enabled with timeout, 2 -> enabled sticky."
|
||||
"dragging will not drop the dragged item.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
.description = "When enabled, lifting the finger off for a short time while dragging will not drop the dragged item.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:touchpad:tap-and-drag",
|
||||
@@ -646,12 +620,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:touchpad:drag_3fg",
|
||||
.description = "Three Finger Drag 0 -> disabled, 1 -> 3 finger, 2 -> 4 finger",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
|
||||
},
|
||||
|
||||
/*
|
||||
* input:touchdevice:
|
||||
@@ -923,18 +891,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{STRVAL_EMPTY}, //##TODO UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_weight_active",
|
||||
.description = "weight of the font used to display active groupbar titles",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"normal"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_weight_inactive",
|
||||
.description = "weight of the font used to display inactive groupbar titles",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"normal"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:font_size",
|
||||
.description = "font size of groupbar title",
|
||||
@@ -953,12 +909,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{14, 1, 64},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:indicator_gap",
|
||||
.description = "height of the gap between the groupbar indicator and title",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 64},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:indicator_height",
|
||||
.description = "height of the groupbar indicator",
|
||||
@@ -1015,28 +965,10 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_color",
|
||||
.description = "color for window titles in the groupbar",
|
||||
.description = "controls the group bar text color",
|
||||
.type = CONFIG_OPTION_COLOR,
|
||||
.data = SConfigOptionDescription::SColorData{0xffffffff},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_color_inactive",
|
||||
.description = "color for inactive windows' titles in the groupbar (if unset, defaults to text_color)",
|
||||
.type = CONFIG_OPTION_COLOR,
|
||||
.data = SConfigOptionDescription::SColorData{}, //TODO: UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_color_locked_active",
|
||||
.description = "color for the active window's title in a locked group (if unset, defaults to text_color)",
|
||||
.type = CONFIG_OPTION_COLOR,
|
||||
.data = SConfigOptionDescription::SColorData{}, //TODO: UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_color_locked_inactive",
|
||||
.description = "color for inactive windows' titles in locked groups (if unset, defaults to text_color_inactive)",
|
||||
.type = CONFIG_OPTION_COLOR,
|
||||
.data = SConfigOptionDescription::SColorData{}, //TODO: UNSET?
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:col.active",
|
||||
.description = "active group border color",
|
||||
@@ -1073,18 +1005,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:keep_upper_gap",
|
||||
.description = "keep an upper gap above gradient",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "group:groupbar:text_offset",
|
||||
.description = "set an offset for a text",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SRangeData{0, -20, 20},
|
||||
},
|
||||
|
||||
/*
|
||||
* misc:
|
||||
@@ -1134,9 +1054,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:vrr",
|
||||
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only, 3 - fullscreen with game or video content type [0/1/2/3]",
|
||||
.description = " controls the VRR (Adaptive Sync) of your monitors. 0 - off, 1 - on, 2 - fullscreen only [0/1/2]",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 3},
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:mouse_move_enables_dpms",
|
||||
@@ -1213,14 +1133,20 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:allow_session_lock_restore",
|
||||
.description = "if true, will allow you to restart a lockscreen app in case it crashes (red screen of death)",
|
||||
.value = "misc:render_ahead_of_time",
|
||||
.description = "[Warning: buggy] starts rendering before your monitor displays a frame in order to lower latency",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:session_lock_xray",
|
||||
.description = "keep rendering workspaces below your lockscreen",
|
||||
.value = "misc:render_ahead_safezone",
|
||||
.description = "how many ms of safezone to add to rendering ahead of time. Recommended 1-2.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 1, 10},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:allow_session_lock_restore",
|
||||
.description = "if true, will allow you to restart a lockscreen app in case it crashes (red screen of death)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
@@ -1291,12 +1217,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:anr_missed_pings",
|
||||
.description = "number of missed pings before showing the ANR dialog",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{1, 1, 10},
|
||||
},
|
||||
|
||||
/*
|
||||
* binds:
|
||||
@@ -1320,12 +1240,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:hide_special_on_workspace_change",
|
||||
.description = "If enabled, changing the active workspace (including to itself) will hide the special workspace on the monitor where the newly active workspace resides.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:allow_workspace_cycles",
|
||||
.description = "If enabled, workspaces don’t forget their previous workspace, so cycles can be created by switching to the first workspace in a sequence, then endlessly "
|
||||
@@ -1382,12 +1296,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "binds:drag_threshold",
|
||||
.description = "Movement threshold in pixels for window dragging and c/g bind flags. 0 to disable and grab on mousedown.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, INT_MAX},
|
||||
},
|
||||
|
||||
/*
|
||||
* xwayland:
|
||||
@@ -1411,12 +1319,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "xwayland:create_abstract_socket",
|
||||
.description = "Create the abstract Unix domain socket for XWayland",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* opengl:
|
||||
@@ -1433,6 +1335,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
* render:
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "render:explicit_sync",
|
||||
.description = "Whether to enable explicit sync support. Requires a hyprland restart. 0 - no, 1 - yes, 2 - auto based on the gpu driver",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:explicit_sync_kms",
|
||||
.description = "Whether to enable explicit sync support for the KMS layer. Requires explicit_sync to be enabled. 0 - no, 1 - yes, 2 - auto based on the gpu driver",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:direct_scanout",
|
||||
.description = "Enables direct scanout. Direct scanout attempts to reduce lag when there is only one fullscreen application on a screen (e.g. game). It is also "
|
||||
@@ -1461,33 +1375,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_fs_passthrough",
|
||||
.description = "Passthrough color settings for fullscreen apps when possible",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_enabled",
|
||||
.description = "Enable Color Management pipelines (requires restart to fully take effect)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:send_content_type",
|
||||
.description = "Report content type to allow monitor profile autoswitch (may result in a black screen during the switch)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_auto_hdr",
|
||||
.description = "Auto-switch to hdr mode when fullscreen app is in hdr, 0 - off, 1 - hdr, 2 - hdredid (cm_fs_passthrough can switch to hdr even when this setting is off)",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 1, .min = 0, .max = 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:new_render_scheduling",
|
||||
.description = "enable new render scheduling, which should improve FPS on underpowered devices. This does not add latency when your PC can keep up.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* cursor:
|
||||
@@ -1495,7 +1385,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:no_hardware_cursors",
|
||||
.description = "disables hardware cursors. Auto = disable when multi-gpu on nvidia",
|
||||
.description = "disables hardware cursors. Auto = disable when tearing",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Auto"},
|
||||
},
|
||||
@@ -1542,13 +1432,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:warp_on_toggle_special",
|
||||
.description = "Move the cursor to the last focused window when toggling a special workspace. Options: 0 (Disabled), 1 (Enabled), "
|
||||
"2 (Force - ignores cursor:no_warps option)",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "Disabled,Enabled,Force"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:default_monitor",
|
||||
.description = "the name of a default monitor for the cursor to be set to on startup (see hyprctl monitors for names)",
|
||||
@@ -1620,12 +1503,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "ecosystem:enforce_permissions",
|
||||
.description = "whether to enable permission control (see https://wiki.hypr.land/Configuring/Permissions/).",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* debug:
|
||||
@@ -1679,6 +1556,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:watchdog_timeout",
|
||||
.description = "sets the timeout in seconds for watchdog to abort processing of a signal of the main thread. Set to 0 to disable.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{5, 0, 20},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "debug:disable_scale_checks",
|
||||
.description = "disables verification of the scale factors. Will result in pixel alignment and rounding errors.",
|
||||
@@ -1795,24 +1678,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "positional,current,opening"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "dwindle:precise_mouse_move",
|
||||
.description = "if enabled, bindm movewindow will drop the window more precisely depending on where your mouse is.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "dwindle:single_window_aspect_ratio",
|
||||
.description = "If specified, whenever only a single window is open, it will be coerced into the specified aspect ratio. Ignored if the y-value is zero.",
|
||||
.type = CONFIG_OPTION_VECTOR,
|
||||
.data = SConfigOptionDescription::SVectorData{{0, 0}, {0, 0}, {1000., 1000.}},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "dwindle:single_window_aspect_ratio_tolerance",
|
||||
.description = "Minimum distance for single_window_aspect_ratio to take effect, in fractions of the monitor's size.",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.1f, 0.f, 1.f},
|
||||
},
|
||||
|
||||
/*
|
||||
* master:
|
||||
@@ -1873,10 +1738,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 10}, //##TODO RANGE?
|
||||
},
|
||||
SConfigOptionDescription{.value = "master:center_master_fallback",
|
||||
.description = "Set fallback for center master when slaves are less than slave_count_for_center_master, can be left ,right ,top ,bottom",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"left"}},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:center_master_slaves_on_right",
|
||||
.description = "set if the slaves should appear on right of master when slave_count_for_center_master > 2",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:center_ignores_reserved",
|
||||
.description = "centers the master window on monitor ignoring reserved areas",
|
||||
@@ -1897,21 +1764,16 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:always_keep_position",
|
||||
.description = "whether to keep the master window in its configured position when there are no slave windows",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* Experimental
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "experimental:xx_color_management_v4",
|
||||
.description = "enable color management protocol",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:always_keep_position",
|
||||
.description = "whether to keep the master window in its configured position when there are no slave windows",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,6 @@
|
||||
|
||||
#define HANDLE void*
|
||||
|
||||
class CConfigManager;
|
||||
|
||||
struct SWorkspaceRule {
|
||||
std::string monitor = "";
|
||||
std::string workspaceString = "";
|
||||
@@ -38,7 +36,6 @@ struct SWorkspaceRule {
|
||||
bool isPersistent = false;
|
||||
std::optional<CCssGapData> gapsIn;
|
||||
std::optional<CCssGapData> gapsOut;
|
||||
std::optional<CCssGapData> floatGaps = gapsOut;
|
||||
std::optional<int64_t> borderSize;
|
||||
std::optional<bool> decorate;
|
||||
std::optional<bool> noRounding;
|
||||
@@ -144,18 +141,8 @@ struct SFirstExecRequest {
|
||||
struct SFloatCache {
|
||||
size_t hash;
|
||||
|
||||
SFloatCache(PHLWINDOW window, bool initial) {
|
||||
// Base hash from class/title
|
||||
size_t baseHash = initial ? (std::hash<std::string>{}(window->m_initialClass) ^ (std::hash<std::string>{}(window->m_initialTitle) << 1)) :
|
||||
(std::hash<std::string>{}(window->m_class) ^ (std::hash<std::string>{}(window->m_title) << 1));
|
||||
|
||||
// Use empty string as default tag value
|
||||
std::string tagValue = "";
|
||||
if (auto xdgTag = window->xdgTag())
|
||||
tagValue = xdgTag.value();
|
||||
|
||||
// Combine hashes
|
||||
hash = baseHash ^ (std::hash<std::string>{}(tagValue) << 2);
|
||||
SFloatCache(PHLWINDOW window) {
|
||||
hash = std::hash<std::string>{}(window->m_szClass) ^ (std::hash<std::string>{}(window->m_szTitle) << 1);
|
||||
}
|
||||
|
||||
bool operator==(const SFloatCache& other) const {
|
||||
@@ -172,32 +159,6 @@ namespace std {
|
||||
};
|
||||
}
|
||||
|
||||
class CMonitorRuleParser {
|
||||
public:
|
||||
CMonitorRuleParser(const std::string& name);
|
||||
|
||||
const std::string& name();
|
||||
SMonitorRule& rule();
|
||||
std::optional<std::string> getError();
|
||||
bool parseMode(const std::string& value);
|
||||
bool parsePosition(const std::string& value, bool isFirst = false);
|
||||
bool parseScale(const std::string& value);
|
||||
bool parseTransform(const std::string& value);
|
||||
bool parseBitdepth(const std::string& value);
|
||||
bool parseCM(const std::string& value);
|
||||
bool parseSDRBrightness(const std::string& value);
|
||||
bool parseSDRSaturation(const std::string& value);
|
||||
bool parseVRR(const std::string& value);
|
||||
|
||||
void setDisabled();
|
||||
void setMirror(const std::string& value);
|
||||
bool setReserved(const SMonitorAdditionalReservedArea& value);
|
||||
|
||||
private:
|
||||
SMonitorRule m_rule;
|
||||
std::string m_error = "";
|
||||
};
|
||||
|
||||
class CConfigManager {
|
||||
public:
|
||||
CConfigManager();
|
||||
@@ -274,6 +235,7 @@ class CConfigManager {
|
||||
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWindowRuleV2(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||
@@ -283,52 +245,48 @@ class CConfigManager {
|
||||
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePermission(const std::string&, const std::string&);
|
||||
|
||||
std::optional<std::string> handleMonitorv2(const std::string& output);
|
||||
Hyprlang::CParseResult handleMonitorv2();
|
||||
std::string configCurrentPath;
|
||||
|
||||
std::string m_configCurrentPath;
|
||||
|
||||
bool m_wantsMonitorReload = false;
|
||||
bool m_noMonitorReload = false;
|
||||
bool m_isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
|
||||
bool m_lastConfigVerificationWasSuccessful = true;
|
||||
bool m_bWantsMonitorReload = false;
|
||||
bool m_bNoMonitorReload = false;
|
||||
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
|
||||
bool m_bLastConfigVerificationWasSuccessful = true;
|
||||
|
||||
void storeFloatingSize(PHLWINDOW window, const Vector2D& size);
|
||||
std::optional<Vector2D> getStoredFloatingSize(PHLWINDOW window);
|
||||
|
||||
private:
|
||||
UP<Hyprlang::CConfig> m_config;
|
||||
UP<Hyprlang::CConfig> m_pConfig;
|
||||
|
||||
std::vector<std::string> m_configPaths;
|
||||
|
||||
Hyprutils::Animation::CAnimationConfigTree m_animationTree;
|
||||
Hyprutils::Animation::CAnimationConfigTree m_AnimationTree;
|
||||
|
||||
std::string m_currentSubmap = ""; // For storing the current keybind submap
|
||||
std::string m_szCurrentSubmap = ""; // For storing the current keybind submap
|
||||
|
||||
std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||
|
||||
std::vector<std::string> m_declaredPlugins;
|
||||
std::vector<SPluginKeyword> m_pluginKeywords;
|
||||
std::vector<SPluginVariable> m_pluginVariables;
|
||||
std::vector<std::string> m_vDeclaredPlugins;
|
||||
std::vector<SPluginKeyword> pluginKeywords;
|
||||
std::vector<SPluginVariable> pluginVariables;
|
||||
|
||||
bool m_isFirstLaunch = true; // For exec-once
|
||||
bool isFirstLaunch = true; // For exec-once
|
||||
|
||||
std::vector<SMonitorRule> m_monitorRules;
|
||||
std::vector<SWorkspaceRule> m_workspaceRules;
|
||||
std::vector<SP<CWindowRule>> m_windowRules;
|
||||
std::vector<SP<CLayerRule>> m_layerRules;
|
||||
std::vector<std::string> m_blurLSNamespaces;
|
||||
std::vector<SMonitorRule> m_vMonitorRules;
|
||||
std::vector<SWorkspaceRule> m_vWorkspaceRules;
|
||||
std::vector<SP<CWindowRule>> m_vWindowRules;
|
||||
std::vector<SP<CLayerRule>> m_vLayerRules;
|
||||
std::vector<std::string> m_dBlurLSNamespaces;
|
||||
|
||||
bool m_firstExecDispatched = false;
|
||||
bool m_manualCrashInitiated = false;
|
||||
bool firstExecDispatched = false;
|
||||
bool m_bManualCrashInitiated = false;
|
||||
|
||||
std::vector<SFirstExecRequest> m_firstExecRequests; // bool is for if with rules
|
||||
std::vector<std::string> m_finalExecRequests;
|
||||
std::vector<SFirstExecRequest> firstExecRequests; // bool is for if with rules
|
||||
std::vector<std::string> finalExecRequests;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> m_failedPluginConfigValues; // for plugin values of unloaded plugins
|
||||
std::string m_configErrors = "";
|
||||
std::vector<std::pair<std::string, std::string>> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins
|
||||
std::string m_szConfigErrors = "";
|
||||
|
||||
uint32_t m_configValueNumber = 0;
|
||||
|
||||
@@ -338,7 +296,6 @@ class CConfigManager {
|
||||
std::optional<std::string> resetHLConfig();
|
||||
std::optional<std::string> generateConfig(std::string configPath);
|
||||
std::optional<std::string> verifyConfigExists();
|
||||
|
||||
void postConfigReload(const Hyprlang::CParseResult& result);
|
||||
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
||||
|
||||
@@ -351,7 +308,6 @@ class CConfigManager {
|
||||
std::unordered_map<SFloatCache, Vector2D> m_mStoredFloatingSizes;
|
||||
|
||||
friend struct SConfigOptionDescription;
|
||||
friend class CMonitorRuleParser;
|
||||
};
|
||||
|
||||
inline UP<CConfigManager> g_pConfigManager;
|
||||
|
@@ -1,15 +0,0 @@
|
||||
#include "ConfigValue.hpp"
|
||||
#include "ConfigManager.hpp"
|
||||
#include "../macros.hpp"
|
||||
|
||||
void local__configValuePopulate(void* const** p, const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
|
||||
*p = PVHYPRLANG->getDataStaticPtr();
|
||||
}
|
||||
|
||||
std::type_index local__configValueTypeIdx(const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
const auto ANY = PVHYPRLANG->getValue();
|
||||
return std::type_index(ANY.type());
|
||||
}
|
@@ -4,19 +4,21 @@
|
||||
#include <typeindex>
|
||||
#include <hyprlang.hpp>
|
||||
#include "../macros.hpp"
|
||||
|
||||
// giga hack to avoid including configManager here
|
||||
// NOLINTNEXTLINE
|
||||
void local__configValuePopulate(void* const** p, const std::string& val);
|
||||
std::type_index local__configValueTypeIdx(const std::string& val);
|
||||
#include "ConfigManager.hpp"
|
||||
|
||||
template <typename T>
|
||||
class CConfigValue {
|
||||
public:
|
||||
CConfigValue(const std::string& val) {
|
||||
const auto PVHYPRLANG = g_pConfigManager->getHyprlangConfigValuePtr(val);
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
p_ = PVHYPRLANG->getDataStaticPtr();
|
||||
|
||||
#ifdef HYPRLAND_DEBUG
|
||||
// verify type
|
||||
const auto TYPE = local__configValueTypeIdx(val);
|
||||
const auto ANY = PVHYPRLANG->getValue();
|
||||
const auto TYPE = std::type_index(ANY.type());
|
||||
|
||||
// exceptions
|
||||
const bool STRINGEX = (typeid(T) == typeid(std::string) && TYPE == typeid(Hyprlang::STRING));
|
||||
@@ -24,8 +26,6 @@ class CConfigValue {
|
||||
|
||||
RASSERT(typeid(T) == TYPE || STRINGEX || CUSTOMEX, "Mismatched type in CConfigValue<T>, got {} but has {}", typeid(T).name(), TYPE.name());
|
||||
#endif
|
||||
|
||||
local__configValuePopulate(&p_, val);
|
||||
}
|
||||
|
||||
T* ptr() const {
|
||||
|
@@ -2,17 +2,302 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
inline constexpr std::string_view AUTOGENERATED_PREFIX = R"#(
|
||||
inline const std::string AUTOCONFIG = R"#(
|
||||
# #######################################################################################
|
||||
# AUTOGENERATED HYPRLAND CONFIG.
|
||||
# EDIT THIS CONFIG ACCORDING TO THE WIKI INSTRUCTIONS.
|
||||
# PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hyprland.conf AND EDIT IT,
|
||||
# OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS.
|
||||
# #######################################################################################
|
||||
|
||||
autogenerated = 1 # remove this line to remove the warning
|
||||
|
||||
)#";
|
||||
inline constexpr char EXAMPLE_CONFIG_BYTES[] = {
|
||||
#embed "../../example/hyprland.conf"
|
||||
};
|
||||
# This is an example Hyprland config file.
|
||||
# Refer to the wiki for more information.
|
||||
# https://wiki.hyprland.org/Configuring/
|
||||
|
||||
inline constexpr std::string_view EXAMPLE_CONFIG = {EXAMPLE_CONFIG_BYTES, sizeof(EXAMPLE_CONFIG_BYTES)};
|
||||
# Please note not all available settings / options are set here.
|
||||
# For a full list, see the wiki
|
||||
|
||||
# You can split this configuration into multiple files
|
||||
# Create your files separately and then link them to this file like this:
|
||||
# source = ~/.config/hypr/myColors.conf
|
||||
|
||||
|
||||
################
|
||||
### MONITORS ###
|
||||
################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Monitors/
|
||||
monitor=,preferred,auto,auto
|
||||
|
||||
|
||||
###################
|
||||
### MY PROGRAMS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
|
||||
# Set programs that you use
|
||||
$terminal = kitty
|
||||
$fileManager = dolphin
|
||||
$menu = wofi --show drun
|
||||
|
||||
|
||||
#################
|
||||
### AUTOSTART ###
|
||||
#################
|
||||
|
||||
# Autostart necessary processes (like notifications daemons, status bars, etc.)
|
||||
# Or execute your favorite apps at launch like this:
|
||||
|
||||
# exec-once = $terminal
|
||||
# exec-once = nm-applet &
|
||||
# exec-once = waybar & hyprpaper & firefox
|
||||
|
||||
|
||||
#############################
|
||||
### ENVIRONMENT VARIABLES ###
|
||||
#############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
|
||||
# Refer to https://wiki.hyprland.org/Configuring/Variables/
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 20
|
||||
|
||||
border_size = 2
|
||||
|
||||
# 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.hyprland.org/Configuring/Tearing/ before you turn this on
|
||||
allow_tearing = false
|
||||
|
||||
layout = dwindle
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
inactive_opacity = 1.0
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
range = 4
|
||||
render_power = 3
|
||||
color = rgba(1a1a1aee)
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#blur
|
||||
blur {
|
||||
enabled = true
|
||||
size = 3
|
||||
passes = 1
|
||||
|
||||
vibrancy = 0.1696
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = yes, please :)
|
||||
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
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.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
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# 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.hyprland.org/Configuring/Master-Layout/ for more
|
||||
master {
|
||||
new_status = master
|
||||
}
|
||||
|
||||
# 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. :(
|
||||
}
|
||||
|
||||
|
||||
#############
|
||||
### INPUT ###
|
||||
#############
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_variant =
|
||||
kb_model =
|
||||
kb_options =
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 1
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
touchpad {
|
||||
natural_scroll = false
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#gestures
|
||||
gestures {
|
||||
workspace_swipe = false
|
||||
}
|
||||
|
||||
# Example per-device config
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
|
||||
device {
|
||||
name = epic-mouse-v1
|
||||
sensitivity = -0.5
|
||||
}
|
||||
|
||||
|
||||
###################
|
||||
### KEYBINDINGS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
$mainMod = SUPER # Sets "Windows" key as main modifier
|
||||
|
||||
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
|
||||
bind = $mainMod, Q, exec, $terminal
|
||||
bind = $mainMod, C, killactive,
|
||||
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, togglesplit, # dwindle
|
||||
|
||||
# Move focus with mainMod + arrow keys
|
||||
bind = $mainMod, left, movefocus, l
|
||||
bind = $mainMod, right, movefocus, r
|
||||
bind = $mainMod, up, movefocus, u
|
||||
bind = $mainMod, down, movefocus, d
|
||||
|
||||
# Switch workspaces with mainMod + [0-9]
|
||||
bind = $mainMod, 1, workspace, 1
|
||||
bind = $mainMod, 2, workspace, 2
|
||||
bind = $mainMod, 3, workspace, 3
|
||||
bind = $mainMod, 4, workspace, 4
|
||||
bind = $mainMod, 5, workspace, 5
|
||||
bind = $mainMod, 6, workspace, 6
|
||||
bind = $mainMod, 7, workspace, 7
|
||||
bind = $mainMod, 8, workspace, 8
|
||||
bind = $mainMod, 9, workspace, 9
|
||||
bind = $mainMod, 0, workspace, 10
|
||||
|
||||
# Move active window to a workspace with mainMod + SHIFT + [0-9]
|
||||
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||
|
||||
# Example special workspace (scratchpad)
|
||||
bind = $mainMod, S, togglespecialworkspace, magic
|
||||
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
|
||||
|
||||
# Scroll through existing workspaces with mainMod + scroll
|
||||
bind = $mainMod, mouse_down, workspace, e+1
|
||||
bind = $mainMod, mouse_up, workspace, e-1
|
||||
|
||||
# Move/resize windows with mainMod + LMB/RMB and dragging
|
||||
bindm = $mainMod, mouse:272, movewindow
|
||||
bindm = $mainMod, mouse:273, resizewindow
|
||||
|
||||
# Laptop multimedia keys for volume and LCD brightness
|
||||
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
bindl = , XF86AudioPause, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPlay, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
|
||||
##############################
|
||||
### WINDOWS AND WORKSPACES ###
|
||||
##############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
)#";
|
||||
|
@@ -109,6 +109,9 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
finalCrashReport += "\nDate: ";
|
||||
finalCrashReport += GIT_COMMIT_DATE;
|
||||
finalCrashReport += "\nFlags:\n";
|
||||
#ifdef LEGACY_RENDERER
|
||||
finalCrashReport += "legacyrenderer\n";
|
||||
#endif
|
||||
#if ISDEBUG
|
||||
finalCrashReport += "debug\n";
|
||||
#endif
|
||||
@@ -127,11 +130,11 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = plugins[i];
|
||||
finalCrashReport += '\t';
|
||||
finalCrashReport += p->m_name;
|
||||
finalCrashReport += p->name;
|
||||
finalCrashReport += " (";
|
||||
finalCrashReport += p->m_author;
|
||||
finalCrashReport += p->author;
|
||||
finalCrashReport += ") ";
|
||||
finalCrashReport += p->m_version;
|
||||
finalCrashReport += p->version;
|
||||
finalCrashReport += '\n';
|
||||
}
|
||||
|
||||
@@ -189,7 +192,7 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
#endif
|
||||
};
|
||||
u_int miblen = sizeof(mib) / sizeof(mib[0]);
|
||||
char exe[PATH_MAX] = "/nonexistent";
|
||||
char exe[PATH_MAX] = "";
|
||||
size_t sz = sizeof(exe);
|
||||
sysctl(mib, miblen, &exe, &sz, NULL, 0);
|
||||
const auto FPATH = std::filesystem::canonical(exe);
|
||||
@@ -239,5 +242,5 @@ void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
|
||||
finalCrashReport += "\n\nLog tail:\n";
|
||||
|
||||
finalCrashReport += std::string_view(Debug::m_rollingLog).substr(Debug::m_rollingLog.find('\n') + 1);
|
||||
finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find('\n') + 1);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,8 @@
|
||||
|
||||
#include <fstream>
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/defer/Promise.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
// exposed for main.cpp
|
||||
@@ -22,14 +20,12 @@ class CHyprCtl {
|
||||
void unregisterCommand(const SP<SHyprCtlCommand>& cmd);
|
||||
std::string getReply(std::string);
|
||||
|
||||
Hyprutils::OS::CFileDescriptor m_socketFD;
|
||||
Hyprutils::OS::CFileDescriptor m_iSocketFD;
|
||||
|
||||
struct {
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
pid_t pid = 0;
|
||||
SP<CPromise<std::string>> pendingPromise;
|
||||
} m_currentRequestParams;
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
} m_sCurrentRequestParams;
|
||||
|
||||
static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format);
|
||||
static std::string getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format);
|
||||
@@ -38,7 +34,7 @@ class CHyprCtl {
|
||||
private:
|
||||
void startHyprCtlSocket();
|
||||
|
||||
std::vector<SP<SHyprCtlCommand>> m_commands;
|
||||
std::vector<SP<SHyprCtlCommand>> m_vCommands;
|
||||
wl_event_source* m_eventSource = nullptr;
|
||||
std::string m_socketPath;
|
||||
};
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include "../managers/AnimationManager.hpp"
|
||||
|
||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||
m_texture = makeShared<CTexture>();
|
||||
m_pTexture = makeShared<CTexture>();
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
@@ -16,13 +16,13 @@ void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs)
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastRenderTimes.emplace_back(durationUs / 1000.f);
|
||||
m_dLastRenderTimes.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_lastRenderTimes.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastRenderTimes.pop_front();
|
||||
if (m_dLastRenderTimes.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastRenderTimes.pop_front();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
@@ -31,13 +31,13 @@ void CHyprMonitorDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float du
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
|
||||
m_dLastRenderTimesNoOverlay.emplace_back(durationUs / 1000.f);
|
||||
|
||||
if (m_lastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastRenderTimesNoOverlay.pop_front();
|
||||
if (m_dLastRenderTimesNoOverlay.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastRenderTimesNoOverlay.pop_front();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
}
|
||||
|
||||
void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
@@ -46,36 +46,36 @@ void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_lastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_lastFrame).count() / 1000.f);
|
||||
m_dLastFrametimes.emplace_back(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f);
|
||||
|
||||
if (m_lastFrametimes.size() > (long unsigned int)pMonitor->m_refreshRate)
|
||||
m_lastFrametimes.pop_front();
|
||||
if (m_dLastFrametimes.size() > (long unsigned int)pMonitor->refreshRate)
|
||||
m_dLastFrametimes.pop_front();
|
||||
|
||||
m_lastFrame = std::chrono::high_resolution_clock::now();
|
||||
m_tpLastFrame = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (!m_monitor)
|
||||
m_monitor = pMonitor;
|
||||
if (!m_pMonitor)
|
||||
m_pMonitor = pMonitor;
|
||||
|
||||
// anim data too
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : g_pCompositor->m_lastMonitor.lock();
|
||||
if (PMONITORFORTICKS == pMonitor) {
|
||||
if (m_lastAnimationTicks.size() > (long unsigned int)PMONITORFORTICKS->m_refreshRate)
|
||||
m_lastAnimationTicks.pop_front();
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_pMostHzMonitor ? g_pHyprRenderer->m_pMostHzMonitor.lock() : g_pCompositor->m_pLastMonitor.lock();
|
||||
if (PMONITORFORTICKS) {
|
||||
if (m_dLastAnimationTicks.size() > (long unsigned int)PMONITORFORTICKS->refreshRate)
|
||||
m_dLastAnimationTicks.pop_front();
|
||||
|
||||
m_lastAnimationTicks.push_back(g_pAnimationManager->m_lastTickTimeMs);
|
||||
m_dLastAnimationTicks.push_back(g_pAnimationManager->m_fLastTickTime);
|
||||
}
|
||||
}
|
||||
|
||||
int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
|
||||
if (!m_monitor)
|
||||
if (!m_pMonitor)
|
||||
return 0;
|
||||
|
||||
// get avg fps
|
||||
float avgFrametime = 0;
|
||||
float maxFrametime = 0;
|
||||
float minFrametime = 9999;
|
||||
for (auto const& ft : m_lastFrametimes) {
|
||||
for (auto const& ft : m_dLastFrametimes) {
|
||||
if (ft > maxFrametime)
|
||||
maxFrametime = ft;
|
||||
if (ft < minFrametime)
|
||||
@@ -83,12 +83,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgFrametime += ft;
|
||||
}
|
||||
float varFrametime = maxFrametime - minFrametime;
|
||||
avgFrametime /= m_lastFrametimes.empty() ? 1 : m_lastFrametimes.size();
|
||||
avgFrametime /= m_dLastFrametimes.size() == 0 ? 1 : m_dLastFrametimes.size();
|
||||
|
||||
float avgRenderTime = 0;
|
||||
float maxRenderTime = 0;
|
||||
float minRenderTime = 9999;
|
||||
for (auto const& rt : m_lastRenderTimes) {
|
||||
for (auto const& rt : m_dLastRenderTimes) {
|
||||
if (rt > maxRenderTime)
|
||||
maxRenderTime = rt;
|
||||
if (rt < minRenderTime)
|
||||
@@ -96,12 +96,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgRenderTime += rt;
|
||||
}
|
||||
float varRenderTime = maxRenderTime - minRenderTime;
|
||||
avgRenderTime /= m_lastRenderTimes.empty() ? 1 : m_lastRenderTimes.size();
|
||||
avgRenderTime /= m_dLastRenderTimes.size() == 0 ? 1 : m_dLastRenderTimes.size();
|
||||
|
||||
float avgRenderTimeNoOverlay = 0;
|
||||
float maxRenderTimeNoOverlay = 0;
|
||||
float minRenderTimeNoOverlay = 9999;
|
||||
for (auto const& rt : m_lastRenderTimesNoOverlay) {
|
||||
for (auto const& rt : m_dLastRenderTimesNoOverlay) {
|
||||
if (rt > maxRenderTimeNoOverlay)
|
||||
maxRenderTimeNoOverlay = rt;
|
||||
if (rt < minRenderTimeNoOverlay)
|
||||
@@ -109,12 +109,12 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgRenderTimeNoOverlay += rt;
|
||||
}
|
||||
float varRenderTimeNoOverlay = maxRenderTimeNoOverlay - minRenderTimeNoOverlay;
|
||||
avgRenderTimeNoOverlay /= m_lastRenderTimes.empty() ? 1 : m_lastRenderTimes.size();
|
||||
avgRenderTimeNoOverlay /= m_dLastRenderTimes.size() == 0 ? 1 : m_dLastRenderTimes.size();
|
||||
|
||||
float avgAnimMgrTick = 0;
|
||||
float maxAnimMgrTick = 0;
|
||||
float minAnimMgrTick = 9999;
|
||||
for (auto const& at : m_lastAnimationTicks) {
|
||||
for (auto const& at : m_dLastAnimationTicks) {
|
||||
if (at > maxAnimMgrTick)
|
||||
maxAnimMgrTick = at;
|
||||
if (at < minAnimMgrTick)
|
||||
@@ -122,13 +122,13 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
avgAnimMgrTick += at;
|
||||
}
|
||||
float varAnimMgrTick = maxAnimMgrTick - minAnimMgrTick;
|
||||
avgAnimMgrTick /= m_lastAnimationTicks.empty() ? 1 : m_lastAnimationTicks.size();
|
||||
avgAnimMgrTick /= m_dLastAnimationTicks.size() == 0 ? 1 : m_dLastAnimationTicks.size();
|
||||
|
||||
const float FPS = 1.f / (avgFrametime / 1000.f); // frametimes are in ms
|
||||
const float idealFPS = m_lastFrametimes.size();
|
||||
const float idealFPS = m_dLastFrametimes.size();
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_cairo);
|
||||
PangoLayout* layoutText = pango_cairo_create_layout(g_pDebugOverlay->m_pCairo);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
|
||||
@@ -137,7 +137,7 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
|
||||
float maxTextW = 0;
|
||||
int fontSize = 0;
|
||||
auto cr = g_pDebugOverlay->m_cairo;
|
||||
auto cr = g_pDebugOverlay->m_pCairo;
|
||||
|
||||
auto showText = [cr, layoutText, pangoFD, &maxTextW, &fontSize](const char* text, int size) {
|
||||
if (fontSize != size) {
|
||||
@@ -163,22 +163,22 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
const int MARGIN_TOP = 8;
|
||||
const int MARGIN_LEFT = 4;
|
||||
cairo_move_to(cr, MARGIN_LEFT, MARGIN_TOP + offset);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
std::string text;
|
||||
showText(m_monitor->m_name.c_str(), 10);
|
||||
showText(m_pMonitor->szName.c_str(), 10);
|
||||
|
||||
if (FPS > idealFPS * 0.95f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 0.2f, 1.f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 0.2f, 1.f, 0.2f, 1.f);
|
||||
else if (FPS > idealFPS * 0.8f)
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 0.2f, 1.f);
|
||||
else
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 0.2f, 0.2f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 0.2f, 0.2f, 1.f);
|
||||
|
||||
text = std::format("{} FPS", (int)FPS);
|
||||
showText(text.c_str(), 16);
|
||||
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_cairo, 1.f, 1.f, 1.f, 1.f);
|
||||
cairo_set_source_rgba(g_pDebugOverlay->m_pCairo, 1.f, 1.f, 1.f, 1.f);
|
||||
|
||||
text = std::format("Avg Frametime: {:.2f}ms (var {:.2f}ms)", avgFrametime, varFrametime);
|
||||
showText(text.c_str(), 10);
|
||||
@@ -198,10 +198,10 @@ int CHyprMonitorDebugOverlay::draw(int offset) {
|
||||
double posX = 0, posY = 0;
|
||||
cairo_get_current_point(cr, &posX, &posY);
|
||||
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
m_lastDrawnBox = {(int)g_pCompositor->m_monitors.front()->m_position.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_monitors.front()->m_position.y + offset + MARGIN_TOP - 1,
|
||||
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
|
||||
g_pHyprRenderer->damageBox(m_lastDrawnBox);
|
||||
g_pHyprRenderer->damageBox(m_wbLastDrawnBox);
|
||||
m_wbLastDrawnBox = {(int)g_pCompositor->m_vMonitors.front()->vecPosition.x + MARGIN_LEFT - 1, (int)g_pCompositor->m_vMonitors.front()->vecPosition.y + offset + MARGIN_TOP - 1,
|
||||
(int)maxTextW + 2, posY - offset - MARGIN_TOP + 2};
|
||||
g_pHyprRenderer->damageBox(m_wbLastDrawnBox);
|
||||
|
||||
return posY - offset;
|
||||
}
|
||||
@@ -212,7 +212,7 @@ void CHyprDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
m_mMonitorOverlays[pMonitor].renderData(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationUs) {
|
||||
@@ -221,7 +221,7 @@ void CHyprDebugOverlay::renderDataNoOverlay(PHLMONITOR pMonitor, float durationU
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
m_mMonitorOverlays[pMonitor].renderDataNoOverlay(pMonitor, durationUs);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
@@ -230,46 +230,49 @@ void CHyprDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||
if (!*PDEBUGOVERLAY)
|
||||
return;
|
||||
|
||||
m_monitorOverlays[pMonitor].frameData(pMonitor);
|
||||
m_mMonitorOverlays[pMonitor].frameData(pMonitor);
|
||||
}
|
||||
|
||||
void CHyprDebugOverlay::draw() {
|
||||
|
||||
const auto PMONITOR = g_pCompositor->m_monitors.front();
|
||||
const auto PMONITOR = g_pCompositor->m_vMonitors.front();
|
||||
|
||||
if (!m_cairoSurface || !m_cairo) {
|
||||
m_cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y);
|
||||
m_cairo = cairo_create(m_cairoSurface);
|
||||
if (!m_pCairoSurface || !m_pCairo) {
|
||||
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y);
|
||||
m_pCairo = cairo_create(m_pCairoSurface);
|
||||
}
|
||||
|
||||
// clear the pixmap
|
||||
cairo_save(m_cairo);
|
||||
cairo_set_operator(m_cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_cairo);
|
||||
cairo_restore(m_cairo);
|
||||
cairo_save(m_pCairo);
|
||||
cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_pCairo);
|
||||
cairo_restore(m_pCairo);
|
||||
|
||||
// draw the things
|
||||
int offsetY = 0;
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
offsetY += m_monitorOverlays[m].draw(offsetY);
|
||||
for (auto const& m : g_pCompositor->m_vMonitors) {
|
||||
offsetY += m_mMonitorOverlays[m].draw(offsetY);
|
||||
offsetY += 5; // for padding between mons
|
||||
}
|
||||
|
||||
cairo_surface_flush(m_cairoSurface);
|
||||
cairo_surface_flush(m_pCairoSurface);
|
||||
|
||||
// copy the data to an OpenGL texture we have
|
||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||
m_texture->allocate();
|
||||
m_texture->bind();
|
||||
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
const auto DATA = cairo_image_surface_get_data(m_pCairoSurface);
|
||||
m_pTexture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
#ifndef GLES2
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
#endif
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = m_texture;
|
||||
data.box = {0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y};
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
|
||||
data.tex = m_pTexture;
|
||||
data.box = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y};
|
||||
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
|
||||
}
|
||||
|
@@ -17,13 +17,13 @@ class CHyprMonitorDebugOverlay {
|
||||
void frameData(PHLMONITOR pMonitor);
|
||||
|
||||
private:
|
||||
std::deque<float> m_lastFrametimes;
|
||||
std::deque<float> m_lastRenderTimes;
|
||||
std::deque<float> m_lastRenderTimesNoOverlay;
|
||||
std::deque<float> m_lastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_lastFrame;
|
||||
PHLMONITORREF m_monitor;
|
||||
CBox m_lastDrawnBox;
|
||||
std::deque<float> m_dLastFrametimes;
|
||||
std::deque<float> m_dLastRenderTimes;
|
||||
std::deque<float> m_dLastRenderTimesNoOverlay;
|
||||
std::deque<float> m_dLastAnimationTicks;
|
||||
std::chrono::high_resolution_clock::time_point m_tpLastFrame;
|
||||
PHLMONITORREF m_pMonitor;
|
||||
CBox m_wbLastDrawnBox;
|
||||
|
||||
friend class CHyprRenderer;
|
||||
};
|
||||
@@ -37,12 +37,12 @@ class CHyprDebugOverlay {
|
||||
void frameData(PHLMONITOR);
|
||||
|
||||
private:
|
||||
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_monitorOverlays;
|
||||
std::map<PHLMONITORREF, CHyprMonitorDebugOverlay> m_mMonitorOverlays;
|
||||
|
||||
cairo_surface_t* m_cairoSurface = nullptr;
|
||||
cairo_t* m_cairo = nullptr;
|
||||
cairo_surface_t* m_pCairoSurface = nullptr;
|
||||
cairo_t* m_pCairo = nullptr;
|
||||
|
||||
SP<CTexture> m_texture;
|
||||
SP<CTexture> m_pTexture;
|
||||
|
||||
friend class CHyprMonitorDebugOverlay;
|
||||
friend class CHyprRenderer;
|
||||
|
@@ -13,7 +13,7 @@ static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||
// preference: Nerd > FontAwesome > text
|
||||
auto eIconBackendChecks = std::array<eIconBackend, 2>{ICONS_BACKEND_NF, ICONS_BACKEND_FA};
|
||||
for (auto iconID : eIconBackendChecks) {
|
||||
auto iconsText = std::ranges::fold_left(ICONS_ARRAY[iconID], std::string(), std::plus<>());
|
||||
auto iconsText = std::accumulate(ICONS_ARRAY[iconID].begin(), ICONS_ARRAY[iconID].end(), std::string());
|
||||
pango_layout_set_text(layout, iconsText.c_str(), -1);
|
||||
if (pango_layout_get_unknown_glyphs_count(layout) == 0)
|
||||
return iconID;
|
||||
@@ -23,24 +23,24 @@ static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||
|
||||
CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
||||
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
|
||||
if (m_notifications.empty())
|
||||
if (m_vNotifications.size() == 0)
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||
g_pHyprRenderer->damageBox(m_bLastDamage);
|
||||
});
|
||||
|
||||
m_texture = makeShared<CTexture>();
|
||||
m_pTexture = makeShared<CTexture>();
|
||||
}
|
||||
|
||||
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
||||
if (m_cairo)
|
||||
cairo_destroy(m_cairo);
|
||||
if (m_cairoSurface)
|
||||
cairo_surface_destroy(m_cairoSurface);
|
||||
if (m_pCairo)
|
||||
cairo_destroy(m_pCairo);
|
||||
if (m_pCairoSurface)
|
||||
cairo_surface_destroy(m_pCairoSurface);
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::addNotification(const std::string& text, const CHyprColor& color, const float timeMs, const eIcons icon, const float fontSize) {
|
||||
const auto PNOTIF = m_notifications.emplace_back(makeUnique<SNotification>()).get();
|
||||
const auto PNOTIF = m_vNotifications.emplace_back(makeUnique<SNotification>()).get();
|
||||
|
||||
PNOTIF->text = icon != eIcons::ICON_NONE ? " " + text /* tiny bit of padding otherwise icon touches text */ : text;
|
||||
PNOTIF->color = color == CHyprColor(0) ? ICONS_COLORS[icon] : color;
|
||||
@@ -49,19 +49,19 @@ void CHyprNotificationOverlay::addNotification(const std::string& text, const CH
|
||||
PNOTIF->icon = icon;
|
||||
PNOTIF->fontSize = fontSize;
|
||||
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
for (auto const& m : g_pCompositor->m_vMonitors) {
|
||||
g_pCompositor->scheduleFrameForMonitor(m);
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::dismissNotifications(const int amount) {
|
||||
if (amount == -1)
|
||||
m_notifications.clear();
|
||||
m_vNotifications.clear();
|
||||
else {
|
||||
const int AMT = std::min(amount, static_cast<int>(m_notifications.size()));
|
||||
const int AMT = std::min(amount, static_cast<int>(m_vNotifications.size()));
|
||||
|
||||
for (int i = 0; i < AMT; ++i) {
|
||||
m_notifications.erase(m_notifications.begin());
|
||||
m_vNotifications.erase(m_vNotifications.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,12 +77,12 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
float offsetY = 10;
|
||||
float maxWidth = 0;
|
||||
|
||||
const auto SCALE = pMonitor->m_scale;
|
||||
const auto MONSIZE = pMonitor->m_transformedSize;
|
||||
const auto SCALE = pMonitor->scale;
|
||||
const auto MONSIZE = pMonitor->vecTransformedSize;
|
||||
|
||||
static auto fontFamily = CConfigValue<std::string>("misc:font_family");
|
||||
|
||||
PangoLayout* layout = pango_cairo_create_layout(m_cairo);
|
||||
PangoLayout* layout = pango_cairo_create_layout(m_pCairo);
|
||||
PangoFontDescription* pangoFD = pango_font_description_new();
|
||||
|
||||
pango_font_description_set_family(pangoFD, (*fontFamily).c_str());
|
||||
@@ -92,9 +92,9 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
const auto iconBackendID = iconBackendFromLayout(layout);
|
||||
const auto PBEZIER = g_pAnimationManager->getBezier("default");
|
||||
|
||||
for (auto const& notif : m_notifications) {
|
||||
for (auto const& notif : m_vNotifications) {
|
||||
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
|
||||
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->m_pixelSize.x * SCALE) / 1920.f)), 8, 40);
|
||||
const auto FONTSIZE = std::clamp((int)(notif->fontSize * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
|
||||
|
||||
// first rect (bg, col)
|
||||
const float FIRSTRECTANIMP =
|
||||
@@ -139,17 +139,17 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
const auto NOTIFSIZE = Vector2D{textW + 20.0 + iconW + 2 * ICONPADFORNOTIF, textH + 10.0};
|
||||
|
||||
// draw rects
|
||||
cairo_set_source_rgba(m_cairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_cairo);
|
||||
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_pCairo);
|
||||
|
||||
cairo_set_source_rgb(m_cairo, 0.f, 0.f, 0.f);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_cairo);
|
||||
cairo_set_source_rgb(m_pCairo, 0.f, 0.f, 0.f);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC, offsetY, NOTIFSIZE.x * SECONDRECTPERC, NOTIFSIZE.y);
|
||||
cairo_fill(m_pCairo);
|
||||
|
||||
cairo_set_source_rgba(m_cairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
|
||||
cairo_fill(m_cairo);
|
||||
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
|
||||
cairo_fill(m_pCairo);
|
||||
|
||||
// draw gradient
|
||||
if (notif->icon != ICON_NONE) {
|
||||
@@ -158,23 +158,23 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC + GRADIENT_SIZE, offsetY);
|
||||
cairo_pattern_add_color_stop_rgba(pattern, 0, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, ICONCOLOR.a / 3.0);
|
||||
cairo_pattern_add_color_stop_rgba(pattern, 1, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, 0);
|
||||
cairo_rectangle(m_cairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, GRADIENT_SIZE, NOTIFSIZE.y);
|
||||
cairo_set_source(m_cairo, pattern);
|
||||
cairo_fill(m_cairo);
|
||||
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, GRADIENT_SIZE, NOTIFSIZE.y);
|
||||
cairo_set_source(m_pCairo, pattern);
|
||||
cairo_fill(m_pCairo);
|
||||
cairo_pattern_destroy(pattern);
|
||||
|
||||
// draw icon
|
||||
cairo_set_source_rgb(m_cairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0));
|
||||
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY - 2 + std::round((NOTIFSIZE.y - iconH) / 2.0));
|
||||
pango_layout_set_text(layout, ICON.c_str(), -1);
|
||||
pango_cairo_show_layout(m_cairo, layout);
|
||||
pango_cairo_show_layout(m_pCairo, layout);
|
||||
}
|
||||
|
||||
// draw text
|
||||
cairo_set_source_rgb(m_cairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_cairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0));
|
||||
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
|
||||
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY - 2 + std::round((NOTIFSIZE.y - textH) / 2.0));
|
||||
pango_layout_set_text(layout, notif->text.c_str(), -1);
|
||||
pango_cairo_show_layout(m_cairo, layout);
|
||||
pango_cairo_show_layout(m_pCairo, layout);
|
||||
|
||||
// adjust offset and move on
|
||||
offsetY += NOTIFSIZE.y + 10;
|
||||
@@ -187,70 +187,73 @@ CBox CHyprNotificationOverlay::drawNotifications(PHLMONITOR pMonitor) {
|
||||
g_object_unref(layout);
|
||||
|
||||
// cleanup notifs
|
||||
std::erase_if(m_notifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||
std::erase_if(m_vNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });
|
||||
|
||||
return CBox{(int)(pMonitor->m_position.x + pMonitor->m_size.x - maxWidth - 20), (int)pMonitor->m_position.y, (int)maxWidth + 20, (int)offsetY + 10};
|
||||
return CBox{(int)(pMonitor->vecPosition.x + pMonitor->vecSize.x - maxWidth - 20), (int)pMonitor->vecPosition.y, (int)maxWidth + 20, (int)offsetY + 10};
|
||||
}
|
||||
|
||||
void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
|
||||
|
||||
const auto MONSIZE = pMonitor->m_transformedSize;
|
||||
const auto MONSIZE = pMonitor->vecTransformedSize;
|
||||
|
||||
if (m_lastMonitor != pMonitor || m_lastSize != MONSIZE || !m_cairo || !m_cairoSurface) {
|
||||
if (m_pLastMonitor != pMonitor || m_vecLastSize != MONSIZE || !m_pCairo || !m_pCairoSurface) {
|
||||
|
||||
if (m_cairo && m_cairoSurface) {
|
||||
cairo_destroy(m_cairo);
|
||||
cairo_surface_destroy(m_cairoSurface);
|
||||
if (m_pCairo && m_pCairoSurface) {
|
||||
cairo_destroy(m_pCairo);
|
||||
cairo_surface_destroy(m_pCairoSurface);
|
||||
}
|
||||
|
||||
m_cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MONSIZE.x, MONSIZE.y);
|
||||
m_cairo = cairo_create(m_cairoSurface);
|
||||
m_lastMonitor = pMonitor;
|
||||
m_lastSize = MONSIZE;
|
||||
m_pCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MONSIZE.x, MONSIZE.y);
|
||||
m_pCairo = cairo_create(m_pCairoSurface);
|
||||
m_pLastMonitor = pMonitor;
|
||||
m_vecLastSize = MONSIZE;
|
||||
}
|
||||
|
||||
// Draw the notifications
|
||||
if (m_notifications.empty())
|
||||
if (m_vNotifications.size() == 0)
|
||||
return;
|
||||
|
||||
// Render to the monitor
|
||||
|
||||
// clear the pixmap
|
||||
cairo_save(m_cairo);
|
||||
cairo_set_operator(m_cairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_cairo);
|
||||
cairo_restore(m_cairo);
|
||||
cairo_save(m_pCairo);
|
||||
cairo_set_operator(m_pCairo, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(m_pCairo);
|
||||
cairo_restore(m_pCairo);
|
||||
|
||||
cairo_surface_flush(m_cairoSurface);
|
||||
cairo_surface_flush(m_pCairoSurface);
|
||||
|
||||
CBox damage = drawNotifications(pMonitor);
|
||||
|
||||
g_pHyprRenderer->damageBox(damage);
|
||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||
g_pHyprRenderer->damageBox(m_bLastDamage);
|
||||
|
||||
g_pCompositor->scheduleFrameForMonitor(pMonitor);
|
||||
|
||||
m_lastDamage = damage;
|
||||
m_bLastDamage = damage;
|
||||
|
||||
// copy the data to an OpenGL texture we have
|
||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
||||
m_texture->allocate();
|
||||
m_texture->bind();
|
||||
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
const auto DATA = cairo_image_surface_get_data(m_pCairoSurface);
|
||||
m_pTexture->allocate();
|
||||
glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
#ifndef GLES2
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||
#endif
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
||||
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = m_texture;
|
||||
data.tex = m_pTexture;
|
||||
data.box = {0, 0, MONSIZE.x, MONSIZE.y};
|
||||
data.a = 1.F;
|
||||
|
||||
g_pHyprRenderer->m_renderPass.add(makeUnique<CTexPassElement>(std::move(data)));
|
||||
g_pHyprRenderer->m_sRenderPass.add(makeShared<CTexPassElement>(data));
|
||||
}
|
||||
|
||||
bool CHyprNotificationOverlay::hasAny() {
|
||||
return !m_notifications.empty();
|
||||
return !m_vNotifications.empty();
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../helpers/time/Timer.hpp"
|
||||
#include "../helpers/Timer.hpp"
|
||||
#include "../render/Texture.hpp"
|
||||
#include "../SharedDefs.hpp"
|
||||
|
||||
@@ -47,17 +47,17 @@ class CHyprNotificationOverlay {
|
||||
|
||||
private:
|
||||
CBox drawNotifications(PHLMONITOR pMonitor);
|
||||
CBox m_lastDamage;
|
||||
CBox m_bLastDamage;
|
||||
|
||||
std::vector<UP<SNotification>> m_notifications;
|
||||
std::vector<UP<SNotification>> m_vNotifications;
|
||||
|
||||
cairo_surface_t* m_cairoSurface = nullptr;
|
||||
cairo_t* m_cairo = nullptr;
|
||||
cairo_surface_t* m_pCairoSurface = nullptr;
|
||||
cairo_t* m_pCairo = nullptr;
|
||||
|
||||
PHLMONITORREF m_lastMonitor;
|
||||
Vector2D m_lastSize = Vector2D(-1, -1);
|
||||
PHLMONITORREF m_pLastMonitor;
|
||||
Vector2D m_vecLastSize = Vector2D(-1, -1);
|
||||
|
||||
SP<CTexture> m_texture;
|
||||
SP<CTexture> m_pTexture;
|
||||
};
|
||||
|
||||
inline UP<CHyprNotificationOverlay> g_pHyprNotificationOverlay;
|
||||
|
@@ -7,21 +7,21 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
void Debug::init(const std::string& IS) {
|
||||
m_logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
m_logOfs.open(m_logFile, std::ios::out | std::ios::app);
|
||||
auto handle = m_logOfs.native_handle();
|
||||
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
logOfs.open(logFile, std::ios::out | std::ios::app);
|
||||
auto handle = logOfs.native_handle();
|
||||
fcntl(handle, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
void Debug::close() {
|
||||
m_logOfs.close();
|
||||
logOfs.close();
|
||||
}
|
||||
|
||||
void Debug::log(eLogLevel level, std::string str) {
|
||||
if (level == TRACE && !m_trace)
|
||||
if (level == TRACE && !trace)
|
||||
return;
|
||||
|
||||
if (m_shuttingDown)
|
||||
if (shuttingDown)
|
||||
return;
|
||||
|
||||
std::string coloredStr = str;
|
||||
@@ -55,20 +55,20 @@ void Debug::log(eLogLevel level, std::string str) {
|
||||
}
|
||||
//NOLINTEND
|
||||
|
||||
m_rollingLog += str + "\n";
|
||||
if (m_rollingLog.size() > ROLLING_LOG_SIZE)
|
||||
m_rollingLog = m_rollingLog.substr(m_rollingLog.size() - ROLLING_LOG_SIZE);
|
||||
rollingLog += str + "\n";
|
||||
if (rollingLog.size() > ROLLING_LOG_SIZE)
|
||||
rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE);
|
||||
|
||||
if (SRollingLogFollow::get().isRunning())
|
||||
SRollingLogFollow::get().addLog(str);
|
||||
|
||||
if (!m_disableLogs || !**m_disableLogs) {
|
||||
if (!disableLogs || !**disableLogs) {
|
||||
// log to a file
|
||||
m_logOfs << str << "\n";
|
||||
m_logOfs.flush();
|
||||
logOfs << str << "\n";
|
||||
logOfs.flush();
|
||||
}
|
||||
|
||||
// log it to the stdout too.
|
||||
if (!m_disableStdout)
|
||||
std::println("{}", ((m_coloredLogs && !**m_coloredLogs) ? str : coloredStr));
|
||||
if (!disableStdout)
|
||||
std::println("{}", ((coloredLogs && !**coloredLogs) ? str : coloredStr));
|
||||
}
|
||||
|
@@ -21,17 +21,17 @@ enum eLogLevel : int8_t {
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
inline std::string m_logFile;
|
||||
inline std::ofstream m_logOfs;
|
||||
inline int64_t* const* m_disableLogs = nullptr;
|
||||
inline int64_t* const* m_disableTime = nullptr;
|
||||
inline bool m_disableStdout = false;
|
||||
inline bool m_trace = false;
|
||||
inline bool m_shuttingDown = false;
|
||||
inline int64_t* const* m_coloredLogs = nullptr;
|
||||
inline std::string logFile;
|
||||
inline std::ofstream logOfs;
|
||||
inline int64_t* const* disableLogs = nullptr;
|
||||
inline int64_t* const* disableTime = nullptr;
|
||||
inline bool disableStdout = false;
|
||||
inline bool trace = false;
|
||||
inline bool shuttingDown = false;
|
||||
inline int64_t* const* coloredLogs = nullptr;
|
||||
|
||||
inline std::string m_rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex m_logMutex;
|
||||
inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex logMutex;
|
||||
|
||||
void init(const std::string& IS);
|
||||
void close();
|
||||
@@ -42,18 +42,18 @@ namespace Debug {
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::lock_guard<std::mutex> guard(m_logMutex);
|
||||
std::lock_guard<std::mutex> guard(logMutex);
|
||||
|
||||
if (level == TRACE && !m_trace)
|
||||
if (level == TRACE && !trace)
|
||||
return;
|
||||
|
||||
if (m_shuttingDown)
|
||||
if (shuttingDown)
|
||||
return;
|
||||
|
||||
std::string logMsg = "";
|
||||
|
||||
// print date and time to the ofs
|
||||
if (m_disableTime && !**m_disableTime) {
|
||||
if (disableTime && !**disableTime) {
|
||||
#ifndef _LIBCPP_VERSION
|
||||
static auto current_zone = std::chrono::current_zone();
|
||||
const auto zt = std::chrono::zoned_time{current_zone, std::chrono::system_clock::now()};
|
||||
|
@@ -5,55 +5,55 @@
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
struct SRollingLogFollow {
|
||||
std::unordered_map<int, std::string> m_socketToRollingLogFollowQueue;
|
||||
std::shared_mutex m_mutex;
|
||||
bool m_running = false;
|
||||
std::unordered_map<int, std::string> socketToRollingLogFollowQueue;
|
||||
std::shared_mutex m;
|
||||
bool running = false;
|
||||
static constexpr size_t ROLLING_LOG_FOLLOW_TOO_BIG = 8192;
|
||||
|
||||
// Returns true if the queue is empty for the given socket
|
||||
bool isEmpty(int socket) {
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return m_socketToRollingLogFollowQueue[socket].empty();
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return socketToRollingLogFollowQueue[socket].empty();
|
||||
}
|
||||
|
||||
std::string debugInfo() {
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return std::format("RollingLogFollow, got {} connections", m_socketToRollingLogFollowQueue.size());
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size());
|
||||
}
|
||||
|
||||
std::string getLog(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
|
||||
const std::string ret = m_socketToRollingLogFollowQueue[socket];
|
||||
m_socketToRollingLogFollowQueue[socket] = "";
|
||||
const std::string ret = socketToRollingLogFollowQueue[socket];
|
||||
socketToRollingLogFollowQueue[socket] = "";
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
void addLog(const std::string& log) {
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_running = true;
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
running = true;
|
||||
std::vector<int> to_erase;
|
||||
for (const auto& p : m_socketToRollingLogFollowQueue)
|
||||
m_socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||
for (const auto& p : socketToRollingLogFollowQueue)
|
||||
socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
std::shared_lock<std::shared_mutex> r(m_mutex);
|
||||
return m_running;
|
||||
std::shared_lock<std::shared_mutex> r(m);
|
||||
return running;
|
||||
}
|
||||
|
||||
void stopFor(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_socketToRollingLogFollowQueue.erase(socket);
|
||||
if (m_socketToRollingLogFollowQueue.empty())
|
||||
m_running = false;
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
socketToRollingLogFollowQueue.erase(socket);
|
||||
if (socketToRollingLogFollowQueue.empty())
|
||||
running = false;
|
||||
}
|
||||
|
||||
void startFor(int socket) {
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
|
||||
m_running = true;
|
||||
std::unique_lock<std::shared_mutex> w(m);
|
||||
socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket);
|
||||
running = true;
|
||||
}
|
||||
|
||||
static SRollingLogFollow& get() {
|
||||
|
@@ -6,21 +6,21 @@ class CLayerSurface;
|
||||
class CMonitor;
|
||||
|
||||
/* Shared pointer to a workspace */
|
||||
using PHLWORKSPACE = SP<CWorkspace>;
|
||||
typedef SP<CWorkspace> PHLWORKSPACE;
|
||||
/* Weak pointer to a workspace */
|
||||
using PHLWORKSPACEREF = WP<CWorkspace>;
|
||||
typedef WP<CWorkspace> PHLWORKSPACEREF;
|
||||
|
||||
/* Shared pointer to a window */
|
||||
using PHLWINDOW = SP<CWindow>;
|
||||
typedef SP<CWindow> PHLWINDOW;
|
||||
/* Weak pointer to a window */
|
||||
using PHLWINDOWREF = WP<CWindow>;
|
||||
typedef WP<CWindow> PHLWINDOWREF;
|
||||
|
||||
/* Shared pointer to a layer surface */
|
||||
using PHLLS = SP<CLayerSurface>;
|
||||
typedef SP<CLayerSurface> PHLLS;
|
||||
/* Weak pointer to a layer surface */
|
||||
using PHLLSREF = WP<CLayerSurface>;
|
||||
typedef WP<CLayerSurface> PHLLSREF;
|
||||
|
||||
/* Shared pointer to a monitor */
|
||||
using PHLMONITOR = SP<CMonitor>;
|
||||
typedef SP<CMonitor> PHLMONITOR;
|
||||
/* Weak pointer to a monitor */
|
||||
using PHLMONITORREF = WP<CMonitor>;
|
||||
typedef WP<CMonitor> PHLMONITORREF;
|
||||
|
@@ -5,36 +5,34 @@
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order"};
|
||||
|
||||
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
|
||||
const bool VALID = RULES.contains(m_rule) || std::ranges::any_of(RULES_PREFIX, [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
|
||||
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : targetNamespace(ns_), rule(rule_) {
|
||||
const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
|
||||
|
||||
if (!VALID)
|
||||
return;
|
||||
|
||||
if (m_rule == "noanim")
|
||||
m_ruleType = RULE_NOANIM;
|
||||
else if (m_rule == "blur")
|
||||
m_ruleType = RULE_BLUR;
|
||||
else if (m_rule == "blurpopups")
|
||||
m_ruleType = RULE_BLURPOPUPS;
|
||||
else if (m_rule == "dimaround")
|
||||
m_ruleType = RULE_DIMAROUND;
|
||||
else if (m_rule.starts_with("ignorealpha"))
|
||||
m_ruleType = RULE_IGNOREALPHA;
|
||||
else if (m_rule.starts_with("ignorezero"))
|
||||
m_ruleType = RULE_IGNOREZERO;
|
||||
else if (m_rule.starts_with("xray"))
|
||||
m_ruleType = RULE_XRAY;
|
||||
else if (m_rule.starts_with("animation"))
|
||||
m_ruleType = RULE_ANIMATION;
|
||||
else if (m_rule.starts_with("order"))
|
||||
m_ruleType = RULE_ORDER;
|
||||
else if (m_rule.starts_with("abovelock"))
|
||||
m_ruleType = RULE_ABOVELOCK;
|
||||
if (rule == "noanim")
|
||||
ruleType = RULE_NOANIM;
|
||||
else if (rule == "blur")
|
||||
ruleType = RULE_BLUR;
|
||||
else if (rule == "blurpopups")
|
||||
ruleType = RULE_BLURPOPUPS;
|
||||
else if (rule == "dimaround")
|
||||
ruleType = RULE_DIMAROUND;
|
||||
else if (rule.starts_with("ignorealpha"))
|
||||
ruleType = RULE_IGNOREALPHA;
|
||||
else if (rule.starts_with("ignorezero"))
|
||||
ruleType = RULE_IGNOREZERO;
|
||||
else if (rule.starts_with("xray"))
|
||||
ruleType = RULE_XRAY;
|
||||
else if (rule.starts_with("animation"))
|
||||
ruleType = RULE_ANIMATION;
|
||||
else if (rule.starts_with("order"))
|
||||
ruleType = RULE_ORDER;
|
||||
else {
|
||||
Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
|
||||
m_ruleType = RULE_INVALID;
|
||||
ruleType = RULE_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,7 +14,6 @@ class CLayerRule {
|
||||
RULE_BLUR,
|
||||
RULE_BLURPOPUPS,
|
||||
RULE_DIMAROUND,
|
||||
RULE_ABOVELOCK,
|
||||
RULE_IGNOREALPHA,
|
||||
RULE_IGNOREZERO,
|
||||
RULE_XRAY,
|
||||
@@ -23,10 +22,10 @@ class CLayerRule {
|
||||
RULE_ZUMBA,
|
||||
};
|
||||
|
||||
eRuleType m_ruleType = RULE_INVALID;
|
||||
eRuleType ruleType = RULE_INVALID;
|
||||
|
||||
const std::string m_targetNamespace;
|
||||
const std::string m_rule;
|
||||
const std::string targetNamespace;
|
||||
const std::string rule;
|
||||
|
||||
CRuleRegexContainer m_targetNamespaceRegex;
|
||||
};
|
||||
CRuleRegexContainer targetNamespaceRegex;
|
||||
};
|
@@ -15,438 +15,424 @@
|
||||
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
|
||||
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
|
||||
|
||||
auto pMonitor = resource->m_monitor.empty() ? g_pCompositor->m_lastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->m_monitor);
|
||||
auto pMonitor = resource->monitor.empty() ? g_pCompositor->m_pLastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->monitor);
|
||||
|
||||
pLS->m_surface->assign(resource->m_surface.lock(), pLS);
|
||||
pLS->surface->assign(resource->surface.lock(), pLS);
|
||||
|
||||
if (!pMonitor) {
|
||||
Debug::log(ERR, "New LS has no monitor??");
|
||||
return pLS;
|
||||
}
|
||||
|
||||
if (pMonitor->m_mirrorOf)
|
||||
pMonitor = g_pCompositor->m_monitors.front();
|
||||
if (pMonitor->pMirrorOf)
|
||||
pMonitor = g_pCompositor->m_vMonitors.front();
|
||||
|
||||
pLS->m_self = pLS;
|
||||
pLS->self = pLS;
|
||||
|
||||
pLS->m_namespace = resource->m_layerNamespace;
|
||||
pLS->szNamespace = resource->layerNamespace;
|
||||
|
||||
pLS->m_layer = resource->m_current.layer;
|
||||
pLS->m_popupHead = CPopup::create(pLS);
|
||||
pLS->m_monitor = pMonitor;
|
||||
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
|
||||
pLS->layer = resource->current.layer;
|
||||
pLS->popupHead = CPopup::create(pLS);
|
||||
pLS->monitor = pMonitor;
|
||||
pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS);
|
||||
|
||||
pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace);
|
||||
pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace);
|
||||
|
||||
g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(0.f, pLS->alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->realSize, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
|
||||
|
||||
pLS->registerCallbacks();
|
||||
|
||||
pLS->m_alpha->setValueAndWarp(0.f);
|
||||
pLS->alpha->setValueAndWarp(0.f);
|
||||
|
||||
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->m_layerNamespace, (int)pLS->m_layer, pMonitor->m_name);
|
||||
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->layerNamespace, (int)pLS->layer, pMonitor->szName);
|
||||
|
||||
return pLS;
|
||||
}
|
||||
|
||||
void CLayerSurface::registerCallbacks() {
|
||||
m_alpha->setUpdateCallback([this](auto) {
|
||||
if (m_dimAround)
|
||||
g_pHyprRenderer->damageMonitor(m_monitor.lock());
|
||||
alpha->setUpdateCallback([this](auto) {
|
||||
if (dimAround)
|
||||
g_pHyprRenderer->damageMonitor(monitor.lock());
|
||||
});
|
||||
}
|
||||
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : m_layerSurface(resource_) {
|
||||
m_listeners.commit = m_layerSurface->m_events.commit.listen([this] { onCommit(); });
|
||||
m_listeners.map = m_layerSurface->m_events.map.listen([this] { onMap(); });
|
||||
m_listeners.unmap = m_layerSurface->m_events.unmap.listen([this] { onUnmap(); });
|
||||
m_listeners.destroy = m_layerSurface->m_events.destroy.listen([this] { onDestroy(); });
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : layerSurface(resource_) {
|
||||
listeners.commit = layerSurface->events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||
listeners.map = layerSurface->events.map.registerListener([this](std::any d) { onMap(); });
|
||||
listeners.unmap = layerSurface->events.unmap.registerListener([this](std::any d) { onUnmap(); });
|
||||
listeners.destroy = layerSurface->events.destroy.registerListener([this](std::any d) { onDestroy(); });
|
||||
|
||||
m_surface = CWLSurface::create();
|
||||
surface = CWLSurface::create();
|
||||
}
|
||||
|
||||
CLayerSurface::~CLayerSurface() {
|
||||
if (!g_pHyprOpenGL)
|
||||
return;
|
||||
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
if (surface)
|
||||
surface->unassign();
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); });
|
||||
std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); });
|
||||
|
||||
for (auto const& mon : g_pCompositor->m_realMonitors) {
|
||||
for (auto& lsl : mon->m_layerSurfaceLayers) {
|
||||
for (auto const& mon : g_pCompositor->m_vRealMonitors) {
|
||||
for (auto& lsl : mon->m_aLayerSurfaceLayers) {
|
||||
std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLayerSurface::onDestroy() {
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)m_layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface.get());
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto PMONITOR = monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
|
||||
|
||||
if (!m_fadingOut) {
|
||||
if (m_mapped) {
|
||||
if (!fadingOut) {
|
||||
if (mapped) {
|
||||
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
|
||||
onUnmap();
|
||||
} else {
|
||||
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
|
||||
if (m_alpha)
|
||||
m_alpha->setValueAndWarp(0.f);
|
||||
m_fadingOut = true;
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
if (alpha)
|
||||
alpha->setValueAndWarp(0.f);
|
||||
fadingOut = true;
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
}
|
||||
}
|
||||
|
||||
m_popupHead.reset();
|
||||
popupHead.reset();
|
||||
|
||||
m_noProcess = true;
|
||||
noProcess = true;
|
||||
|
||||
// rearrange to fix the reserved areas
|
||||
if (PMONITOR) {
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
|
||||
// and damage
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
}
|
||||
|
||||
m_readyToDelete = true;
|
||||
m_layerSurface.reset();
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
readyToDelete = true;
|
||||
layerSurface.reset();
|
||||
if (surface)
|
||||
surface->unassign();
|
||||
|
||||
m_listeners.unmap.reset();
|
||||
m_listeners.destroy.reset();
|
||||
m_listeners.map.reset();
|
||||
m_listeners.commit.reset();
|
||||
listeners.unmap.reset();
|
||||
listeners.destroy.reset();
|
||||
listeners.map.reset();
|
||||
listeners.commit.reset();
|
||||
}
|
||||
|
||||
void CLayerSurface::onMap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)m_layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface.get());
|
||||
|
||||
m_mapped = true;
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
mapped = true;
|
||||
interactivity = layerSurface->current.interactivity;
|
||||
|
||||
m_layerSurface->m_surface->map();
|
||||
layerSurface->surface->map();
|
||||
|
||||
// this layer might be re-mapped.
|
||||
m_fadingOut = false;
|
||||
g_pCompositor->removeFromFadingOutSafe(m_self.lock());
|
||||
fadingOut = false;
|
||||
g_pCompositor->removeFromFadingOutSafe(self.lock());
|
||||
|
||||
// fix if it changed its mon
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto PMONITOR = monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
applyRules();
|
||||
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
|
||||
m_surface->resource()->enter(PMONITOR->m_self.lock());
|
||||
surface->resource()->enter(PMONITOR->self.lock());
|
||||
|
||||
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
|
||||
if (ISEXCLUSIVE)
|
||||
g_pInputManager->m_exclusiveLSes.push_back(m_self);
|
||||
g_pInputManager->m_dExclusiveLSes.push_back(self);
|
||||
|
||||
const bool GRABSFOCUS = ISEXCLUSIVE ||
|
||||
(m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
||||
(layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE &&
|
||||
// don't focus if constrained
|
||||
(g_pSeatManager->m_mouse.expired() || !g_pInputManager->isConstrained()));
|
||||
(g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()));
|
||||
|
||||
if (GRABSFOCUS) {
|
||||
// TODO: use the new superb really very cool grab
|
||||
if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_surface->resource()))
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
g_pCompositor->focusSurface(surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
|
||||
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
|
||||
g_pInputManager->m_bEmptyFocusCursorSet = false;
|
||||
}
|
||||
|
||||
m_position = Vector2D(m_geometry.x, m_geometry.y);
|
||||
position = Vector2D(geometry.x, geometry.y);
|
||||
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
const bool FULLSCREEN = PMONITOR->m_activeWorkspace && PMONITOR->m_activeWorkspace->m_hasFullscreenWindow && PMONITOR->m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN;
|
||||
const bool FULLSCREEN = PMONITOR->activeWorkspace && PMONITOR->activeWorkspace->m_bHasFullscreenWindow && PMONITOR->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
|
||||
|
||||
startAnimation(!(m_layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
|
||||
m_readyToDelete = false;
|
||||
m_fadingOut = false;
|
||||
startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS));
|
||||
readyToDelete = false;
|
||||
fadingOut = false;
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace});
|
||||
EMIT_HOOK_EVENT("openLayer", m_self.lock());
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", szNamespace});
|
||||
EMIT_HOOK_EVENT("openLayer", self.lock());
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::onUnmap() {
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)m_layerSurface.get());
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", (uintptr_t)layerSurface.get());
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", m_self.lock());
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", layerSurface->layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", self.lock());
|
||||
|
||||
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; });
|
||||
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
|
||||
|
||||
if (!m_monitor || g_pCompositor->m_unsafeState) {
|
||||
if (!monitor || g_pCompositor->m_bUnsafeState) {
|
||||
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
|
||||
m_mapped = false;
|
||||
if (m_layerSurface && m_layerSurface->m_surface)
|
||||
m_layerSurface->m_surface->unmap();
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
|
||||
startAnimation(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// end any pending animations so that snapshot has right dimensions
|
||||
m_realPosition->warp();
|
||||
m_realSize->warp();
|
||||
|
||||
// make a snapshot and start fade
|
||||
g_pHyprRenderer->makeSnapshot(m_self.lock());
|
||||
g_pHyprRenderer->makeLayerSnapshot(self.lock());
|
||||
|
||||
startAnimation(false);
|
||||
|
||||
m_mapped = false;
|
||||
if (m_layerSurface && m_layerSurface->m_surface)
|
||||
m_layerSurface->m_surface->unmap();
|
||||
mapped = false;
|
||||
if (layerSurface && layerSurface->surface)
|
||||
layerSurface->surface->unmap();
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
g_pCompositor->addToFadingOutSafe(self.lock());
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto PMONITOR = monitor.lock();
|
||||
|
||||
const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_surface->resource() || g_pSeatManager->m_state.pointerFocus == m_surface->resource();
|
||||
const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == surface->resource() || g_pSeatManager->state.pointerFocus == surface->resource();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
// refocus if needed
|
||||
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus->m_hlSurface && !g_pCompositor->m_lastFocus->m_hlSurface->keyboardFocusable())) {
|
||||
if (!g_pInputManager->refocusLastWindow(PMONITOR))
|
||||
g_pInputManager->refocus();
|
||||
} else if (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus != m_surface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_lastFocus.lock());
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus->hlSurface && !g_pCompositor->m_pLastFocus->hlSurface->keyboardFocusable()))
|
||||
g_pInputManager->refocusLastWindow(PMONITOR);
|
||||
else if (g_pCompositor->m_pLastFocus && g_pCompositor->m_pLastFocus != surface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_pLastFocus.lock());
|
||||
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
geomFixed = {m_geometry.x + (int)PMONITOR->m_position.x, m_geometry.y + (int)PMONITOR->m_position.y, (int)m_layerSurface->m_surface->m_current.size.x,
|
||||
(int)m_layerSurface->m_surface->m_current.size.y};
|
||||
geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.size.x,
|
||||
(int)layerSurface->surface->current.size.y};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
}
|
||||
|
||||
void CLayerSurface::onCommit() {
|
||||
if (!m_layerSurface)
|
||||
if (!layerSurface)
|
||||
return;
|
||||
|
||||
if (!m_mapped) {
|
||||
if (!mapped) {
|
||||
// we're re-mapping if this is the case
|
||||
if (m_layerSurface->m_surface && !m_layerSurface->m_surface->m_current.texture) {
|
||||
m_fadingOut = false;
|
||||
m_geometry = {};
|
||||
if (layerSurface->surface && !layerSurface->surface->current.texture) {
|
||||
fadingOut = false;
|
||||
geometry = {};
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(monitorID());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
const auto PMONITOR = monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
|
||||
|
||||
CBox geomFixed = {m_geometry.x, m_geometry.y, m_geometry.width, m_geometry.height};
|
||||
CBox geomFixed = {geometry.x, geometry.y, geometry.width, geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
if (m_layerSurface->m_current.committed != 0) {
|
||||
if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
|
||||
if (layerSurface->current.committed != 0) {
|
||||
if (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
|
||||
|
||||
for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) {
|
||||
if (*it == m_self) {
|
||||
if (m_layerSurface->m_current.layer == m_layer)
|
||||
for (auto it = PMONITOR->m_aLayerSurfaceLayers[layer].begin(); it != PMONITOR->m_aLayerSurfaceLayers[layer].end(); it++) {
|
||||
if (*it == self) {
|
||||
if (layerSurface->current.layer == layer)
|
||||
break;
|
||||
PMONITOR->m_layerSurfaceLayers[m_layerSurface->m_current.layer].emplace_back(*it);
|
||||
PMONITOR->m_layerSurfaceLayers[m_layer].erase(it);
|
||||
PMONITOR->m_aLayerSurfaceLayers[layerSurface->current.layer].emplace_back(*it);
|
||||
PMONITOR->m_aLayerSurfaceLayers[layer].erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_layer = m_layerSurface->m_current.layer;
|
||||
layer = layerSurface->current.layer;
|
||||
|
||||
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
|
||||
}
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID);
|
||||
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
PMONITOR->scheduledRecalc = true;
|
||||
} else {
|
||||
m_position = Vector2D(m_geometry.x, m_geometry.y);
|
||||
position = Vector2D(geometry.x, geometry.y);
|
||||
|
||||
// update geom if it changed
|
||||
if (m_layerSurface->m_surface->m_current.scale == 1 && PMONITOR->m_scale != 1.f && m_layerSurface->m_surface->m_current.viewport.hasDestination) {
|
||||
if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.hasDestination) {
|
||||
// fractional scaling. Dirty hack.
|
||||
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.viewport.destination};
|
||||
geometry = {geometry.pos(), layerSurface->surface->current.viewport.destination};
|
||||
} else {
|
||||
// this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly.
|
||||
m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_current.size};
|
||||
geometry = {geometry.pos(), layerSurface->surface->current.size};
|
||||
}
|
||||
}
|
||||
|
||||
if (m_realPosition->goal() != m_geometry.pos()) {
|
||||
if (m_realPosition->isBeingAnimated())
|
||||
*m_realPosition = m_geometry.pos();
|
||||
if (realPosition->goal() != geometry.pos()) {
|
||||
if (realPosition->isBeingAnimated())
|
||||
*realPosition = geometry.pos();
|
||||
else
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
realPosition->setValueAndWarp(geometry.pos());
|
||||
}
|
||||
if (m_realSize->goal() != m_geometry.size()) {
|
||||
if (m_realSize->isBeingAnimated())
|
||||
*m_realSize = m_geometry.size();
|
||||
if (realSize->goal() != geometry.size()) {
|
||||
if (realSize->isBeingAnimated())
|
||||
*realSize = geometry.size();
|
||||
else
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
realSize->setValueAndWarp(geometry.size());
|
||||
}
|
||||
|
||||
if (m_mapped && (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
|
||||
if (mapped && (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) {
|
||||
bool WASLASTFOCUS = false;
|
||||
m_layerSurface->m_surface->breadthfirst(
|
||||
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->m_state.keyboardFocus == surf; },
|
||||
layerSurface->surface->breadthfirst(
|
||||
[&WASLASTFOCUS](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->state.keyboardFocus == surf; },
|
||||
nullptr);
|
||||
if (!WASLASTFOCUS && m_popupHead) {
|
||||
m_popupHead->breadthfirst(
|
||||
if (!WASLASTFOCUS && popupHead) {
|
||||
popupHead->breadthfirst(
|
||||
[&WASLASTFOCUS](WP<CPopup> popup, void* data) {
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->m_wlSurface && g_pSeatManager->m_state.keyboardFocus == popup->m_wlSurface->resource());
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->m_pWLSurface && g_pSeatManager->state.keyboardFocus == popup->m_pWLSurface->resource());
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
const bool WASEXCLUSIVE = m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool WASEXCLUSIVE = interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
|
||||
if (!WASEXCLUSIVE && ISEXCLUSIVE)
|
||||
g_pInputManager->m_exclusiveLSes.push_back(m_self);
|
||||
g_pInputManager->m_dExclusiveLSes.push_back(self);
|
||||
else if (WASEXCLUSIVE && !ISEXCLUSIVE)
|
||||
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == m_self.lock(); });
|
||||
std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); });
|
||||
|
||||
// if the surface was focused and interactive but now isn't, refocus
|
||||
if (WASLASTFOCUS && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
|
||||
if (WASLASTFOCUS && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
|
||||
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
|
||||
// so unfocus the surface here.
|
||||
g_pCompositor->focusSurface(nullptr);
|
||||
g_pInputManager->refocusLastWindow(m_monitor.lock());
|
||||
} else if (WASLASTFOCUS && WASEXCLUSIVE && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||
g_pInputManager->refocusLastWindow(monitor.lock());
|
||||
} else if (WASLASTFOCUS && WASEXCLUSIVE && layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
} else if (!WASEXCLUSIVE && ISEXCLUSIVE) {
|
||||
// if now exclusive and not previously
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
g_pCompositor->focusSurface(surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y);
|
||||
g_pSeatManager->setPointerFocus(surface->resource(), LOCAL);
|
||||
g_pInputManager->m_bEmptyFocusCursorSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
interactivity = layerSurface->current.interactivity;
|
||||
|
||||
g_pHyprRenderer->damageSurface(m_surface->resource(), m_position.x, m_position.y);
|
||||
g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y);
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::applyRules() {
|
||||
m_noAnimations = false;
|
||||
m_forceBlur = false;
|
||||
m_ignoreAlpha = false;
|
||||
m_ignoreAlphaValue = 0.f;
|
||||
m_dimAround = false;
|
||||
m_xray = -1;
|
||||
m_animationStyle.reset();
|
||||
noAnimations = false;
|
||||
forceBlur = false;
|
||||
ignoreAlpha = false;
|
||||
ignoreAlphaValue = 0.f;
|
||||
dimAround = false;
|
||||
xray = -1;
|
||||
animationStyle.reset();
|
||||
|
||||
for (auto const& rule : g_pConfigManager->getMatchingRules(m_self.lock())) {
|
||||
switch (rule->m_ruleType) {
|
||||
for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) {
|
||||
switch (rule->ruleType) {
|
||||
case CLayerRule::RULE_NOANIM: {
|
||||
m_noAnimations = true;
|
||||
noAnimations = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_BLUR: {
|
||||
m_forceBlur = true;
|
||||
forceBlur = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_BLURPOPUPS: {
|
||||
m_forceBlurPopups = true;
|
||||
forceBlurPopups = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_IGNOREALPHA:
|
||||
case CLayerRule::RULE_IGNOREZERO: {
|
||||
const auto FIRST_SPACE_POS = rule->m_rule.find_first_of(' ');
|
||||
const auto FIRST_SPACE_POS = rule->rule.find_first_of(' ');
|
||||
std::string alphaValue = "";
|
||||
if (FIRST_SPACE_POS != std::string::npos)
|
||||
alphaValue = rule->m_rule.substr(FIRST_SPACE_POS + 1);
|
||||
alphaValue = rule->rule.substr(FIRST_SPACE_POS + 1);
|
||||
|
||||
try {
|
||||
m_ignoreAlpha = true;
|
||||
ignoreAlpha = true;
|
||||
if (!alphaValue.empty())
|
||||
m_ignoreAlphaValue = std::stof(alphaValue);
|
||||
ignoreAlphaValue = std::stof(alphaValue);
|
||||
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_DIMAROUND: {
|
||||
m_dimAround = true;
|
||||
dimAround = true;
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_XRAY: {
|
||||
CVarList vars{rule->m_rule, 0, ' '};
|
||||
m_xray = configStringToInt(vars[1]).value_or(false);
|
||||
|
||||
CVarList vars{rule->rule, 0, ' '};
|
||||
try {
|
||||
xray = configStringToInt(vars[1]).value_or(false);
|
||||
} catch (...) {}
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ANIMATION: {
|
||||
CVarList vars{rule->m_rule, 2, 's'};
|
||||
m_animationStyle = vars[1];
|
||||
CVarList vars{rule->rule, 2, 's'};
|
||||
animationStyle = vars[1];
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ORDER: {
|
||||
CVarList vars{rule->m_rule, 2, 's'};
|
||||
CVarList vars{rule->rule, 2, 's'};
|
||||
try {
|
||||
m_order = std::stoi(vars[1]);
|
||||
order = std::stoi(vars[1]);
|
||||
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
|
||||
break;
|
||||
}
|
||||
case CLayerRule::RULE_ABOVELOCK: {
|
||||
m_aboveLockscreen = true;
|
||||
|
||||
CVarList vars{rule->m_rule, 0, ' '};
|
||||
m_aboveLockscreenInteractable = configStringToInt(vars[1]).value_or(false);
|
||||
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -454,19 +440,19 @@ void CLayerSurface::applyRules() {
|
||||
|
||||
void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
if (in) {
|
||||
m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"));
|
||||
realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn"));
|
||||
alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"));
|
||||
} else {
|
||||
m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
|
||||
realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut"));
|
||||
alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut"));
|
||||
}
|
||||
|
||||
const auto ANIMSTYLE = m_animationStyle.value_or(m_realPosition->getStyle());
|
||||
const auto ANIMSTYLE = animationStyle.value_or(realPosition->getStyle());
|
||||
if (ANIMSTYLE.starts_with("slide")) {
|
||||
// get closest edge
|
||||
const auto MIDDLE = m_geometry.middle();
|
||||
const auto MIDDLE = geometry.middle();
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE);
|
||||
|
||||
@@ -486,10 +472,10 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
}
|
||||
|
||||
const std::array<Vector2D, 4> edgePoints = {
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, 0.0},
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x / 2, PMONITOR->m_size.y},
|
||||
PMONITOR->m_position + Vector2D{0.0, PMONITOR->m_size.y},
|
||||
PMONITOR->m_position + Vector2D{PMONITOR->m_size.x, PMONITOR->m_size.y / 2},
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, 0.0},
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x / 2, PMONITOR->vecSize.y},
|
||||
PMONITOR->vecPosition + Vector2D{0.0, PMONITOR->vecSize.y},
|
||||
PMONITOR->vecPosition + Vector2D{PMONITOR->vecSize.x, PMONITOR->vecSize.y / 2},
|
||||
};
|
||||
|
||||
float closest = std::numeric_limits<float>::max();
|
||||
@@ -504,38 +490,38 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
}
|
||||
}
|
||||
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
m_alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
realSize->setValueAndWarp(geometry.size());
|
||||
alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*alpha = in ? 1.f : 0.f;
|
||||
|
||||
Vector2D prePos;
|
||||
|
||||
switch (leader) {
|
||||
case 0:
|
||||
// TOP
|
||||
prePos = {m_geometry.x, PMONITOR->m_position.y - m_geometry.h};
|
||||
prePos = {geometry.x, PMONITOR->vecPosition.y - geometry.h};
|
||||
break;
|
||||
case 1:
|
||||
// BOTTOM
|
||||
prePos = {m_geometry.x, PMONITOR->m_position.y + PMONITOR->m_size.y};
|
||||
prePos = {geometry.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y};
|
||||
break;
|
||||
case 2:
|
||||
// LEFT
|
||||
prePos = {PMONITOR->m_position.x - m_geometry.w, m_geometry.y};
|
||||
prePos = {PMONITOR->vecPosition.x - geometry.w, geometry.y};
|
||||
break;
|
||||
case 3:
|
||||
// RIGHT
|
||||
prePos = {PMONITOR->m_position.x + PMONITOR->m_size.x, m_geometry.y};
|
||||
prePos = {PMONITOR->vecPosition.x + PMONITOR->vecSize.x, geometry.y};
|
||||
break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
|
||||
if (in) {
|
||||
m_realPosition->setValueAndWarp(prePos);
|
||||
*m_realPosition = m_geometry.pos();
|
||||
realPosition->setValueAndWarp(prePos);
|
||||
*realPosition = geometry.pos();
|
||||
} else {
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
*m_realPosition = prePos;
|
||||
realPosition->setValueAndWarp(geometry.pos());
|
||||
*realPosition = prePos;
|
||||
}
|
||||
|
||||
} else if (ANIMSTYLE.starts_with("popin")) {
|
||||
@@ -551,62 +537,62 @@ void CLayerSurface::startAnimation(bool in, bool instant) {
|
||||
|
||||
minPerc *= 0.01;
|
||||
|
||||
const auto GOALSIZE = (m_geometry.size() * minPerc).clamp({5, 5});
|
||||
const auto GOALPOS = m_geometry.pos() + (m_geometry.size() - GOALSIZE) / 2.f;
|
||||
const auto GOALSIZE = (geometry.size() * minPerc).clamp({5, 5});
|
||||
const auto GOALPOS = geometry.pos() + (geometry.size() - GOALSIZE) / 2.f;
|
||||
|
||||
m_alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
alpha->setValueAndWarp(in ? 0.f : 1.f);
|
||||
*alpha = in ? 1.f : 0.f;
|
||||
|
||||
if (in) {
|
||||
m_realSize->setValueAndWarp(GOALSIZE);
|
||||
m_realPosition->setValueAndWarp(GOALPOS);
|
||||
*m_realSize = m_geometry.size();
|
||||
*m_realPosition = m_geometry.pos();
|
||||
realSize->setValueAndWarp(GOALSIZE);
|
||||
realPosition->setValueAndWarp(GOALPOS);
|
||||
*realSize = geometry.size();
|
||||
*realPosition = geometry.pos();
|
||||
} else {
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
*m_realSize = GOALSIZE;
|
||||
*m_realPosition = GOALPOS;
|
||||
realSize->setValueAndWarp(geometry.size());
|
||||
realPosition->setValueAndWarp(geometry.pos());
|
||||
*realSize = GOALSIZE;
|
||||
*realPosition = GOALPOS;
|
||||
}
|
||||
} else {
|
||||
// fade
|
||||
m_realPosition->setValueAndWarp(m_geometry.pos());
|
||||
m_realSize->setValueAndWarp(m_geometry.size());
|
||||
*m_alpha = in ? 1.f : 0.f;
|
||||
realPosition->setValueAndWarp(geometry.pos());
|
||||
realSize->setValueAndWarp(geometry.size());
|
||||
*alpha = in ? 1.f : 0.f;
|
||||
}
|
||||
|
||||
if (!in)
|
||||
m_fadingOut = true;
|
||||
fadingOut = true;
|
||||
}
|
||||
|
||||
bool CLayerSurface::isFadedOut() {
|
||||
if (!m_fadingOut)
|
||||
if (!fadingOut)
|
||||
return false;
|
||||
|
||||
return !m_realPosition->isBeingAnimated() && !m_realSize->isBeingAnimated() && !m_alpha->isBeingAnimated();
|
||||
return !realPosition->isBeingAnimated() && !realSize->isBeingAnimated() && !alpha->isBeingAnimated();
|
||||
}
|
||||
|
||||
int CLayerSurface::popupsCount() {
|
||||
if (!m_layerSurface || !m_mapped || m_fadingOut)
|
||||
if (!layerSurface || !mapped || fadingOut)
|
||||
return 0;
|
||||
|
||||
int no = -1; // we have one dummy
|
||||
m_popupHead->breadthfirst([](WP<CPopup> p, void* data) { *(int*)data += 1; }, &no);
|
||||
popupHead->breadthfirst([](WP<CPopup> p, void* data) { *(int*)data += 1; }, &no);
|
||||
return no;
|
||||
}
|
||||
|
||||
MONITORID CLayerSurface::monitorID() {
|
||||
return m_monitor ? m_monitor->m_id : MONITOR_INVALID;
|
||||
return monitor ? monitor->ID : MONITOR_INVALID;
|
||||
}
|
||||
|
||||
pid_t CLayerSurface::getPID() {
|
||||
pid_t PID = -1;
|
||||
|
||||
if (!m_layerSurface || !m_layerSurface->m_surface || !m_layerSurface->m_surface->getResource() || !m_layerSurface->m_surface->getResource()->resource() ||
|
||||
!m_layerSurface->m_surface->getResource()->resource()->client)
|
||||
if (!layerSurface || !layerSurface->surface || !layerSurface->surface->getResource() || !layerSurface->surface->getResource()->resource() ||
|
||||
!layerSurface->surface->getResource()->resource()->client)
|
||||
return -1;
|
||||
|
||||
wl_client_get_credentials(m_layerSurface->m_surface->getResource()->resource()->client, &PID, nullptr, nullptr);
|
||||
wl_client_get_credentials(layerSurface->surface->getResource()->resource()->client, &PID, nullptr, nullptr);
|
||||
|
||||
return PID;
|
||||
}
|
||||
|
@@ -22,45 +22,44 @@ class CLayerSurface {
|
||||
bool isFadedOut();
|
||||
int popupsCount();
|
||||
|
||||
PHLANIMVAR<Vector2D> m_realPosition;
|
||||
PHLANIMVAR<Vector2D> m_realSize;
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
PHLANIMVAR<Vector2D> realPosition;
|
||||
PHLANIMVAR<Vector2D> realSize;
|
||||
PHLANIMVAR<float> alpha;
|
||||
|
||||
WP<CLayerShellResource> m_layerSurface;
|
||||
WP<CLayerShellResource> layerSurface;
|
||||
wl_list link;
|
||||
|
||||
// the header providing the enum type cannot be imported here
|
||||
int m_interactivity = 0;
|
||||
int interactivity = 0;
|
||||
|
||||
SP<CWLSurface> m_surface;
|
||||
SP<CWLSurface> surface;
|
||||
|
||||
bool m_mapped = false;
|
||||
uint32_t m_layer = 0;
|
||||
bool mapped = false;
|
||||
uint32_t layer = 0;
|
||||
|
||||
PHLMONITORREF m_monitor;
|
||||
PHLMONITORREF monitor;
|
||||
|
||||
bool m_fadingOut = false;
|
||||
bool m_readyToDelete = false;
|
||||
bool m_noProcess = false;
|
||||
bool m_noAnimations = false;
|
||||
bool fadingOut = false;
|
||||
bool readyToDelete = false;
|
||||
bool noProcess = false;
|
||||
bool noAnimations = false;
|
||||
|
||||
bool m_forceBlur = false;
|
||||
bool m_forceBlurPopups = false;
|
||||
int64_t m_xray = -1;
|
||||
bool m_ignoreAlpha = false;
|
||||
float m_ignoreAlphaValue = 0.f;
|
||||
bool m_dimAround = false;
|
||||
int64_t m_order = 0;
|
||||
bool m_aboveLockscreen = false;
|
||||
bool m_aboveLockscreenInteractable = false;
|
||||
bool forceBlur = false;
|
||||
bool forceBlurPopups = false;
|
||||
int64_t xray = -1;
|
||||
bool ignoreAlpha = false;
|
||||
float ignoreAlphaValue = 0.f;
|
||||
bool dimAround = false;
|
||||
int64_t order = 0;
|
||||
|
||||
std::optional<std::string> m_animationStyle;
|
||||
std::optional<std::string> animationStyle;
|
||||
|
||||
PHLLSREF m_self;
|
||||
PHLLSREF self;
|
||||
|
||||
CBox m_geometry = {0, 0, 0, 0};
|
||||
Vector2D m_position;
|
||||
std::string m_namespace = "";
|
||||
UP<CPopup> m_popupHead;
|
||||
CBox geometry = {0, 0, 0, 0};
|
||||
Vector2D position;
|
||||
std::string szNamespace = "";
|
||||
UP<CPopup> popupHead;
|
||||
|
||||
pid_t getPID();
|
||||
|
||||
@@ -76,12 +75,12 @@ class CLayerSurface {
|
||||
CHyprSignalListener map;
|
||||
CHyprSignalListener unmap;
|
||||
CHyprSignalListener commit;
|
||||
} m_listeners;
|
||||
} listeners;
|
||||
|
||||
void registerCallbacks();
|
||||
|
||||
// For the list lookup
|
||||
bool operator==(const CLayerSurface& rhs) const {
|
||||
return m_layerSurface == rhs.m_layerSurface && m_monitor == rhs.m_monitor;
|
||||
return layerSurface == rhs.layerSurface && monitor == rhs.monitor;
|
||||
}
|
||||
};
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include "../protocols/XDGShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../managers/SeatManager.hpp"
|
||||
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
@@ -12,32 +13,32 @@
|
||||
#include <ranges>
|
||||
|
||||
UP<CPopup> CPopup::create(PHLWINDOW pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_windowOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_pWindowOwner = pOwner;
|
||||
popup->m_pSelf = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
UP<CPopup> CPopup::create(PHLLS pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_layerOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_pLayerOwner = pOwner;
|
||||
popup->m_pSelf = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
UP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_resource = resource;
|
||||
popup->m_windowOwner = pOwner->m_windowOwner;
|
||||
popup->m_layerOwner = pOwner->m_layerOwner;
|
||||
popup->m_parent = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->m_wlSurface = CWLSurface::create();
|
||||
popup->m_wlSurface->assign(resource->m_surface->m_surface.lock(), popup.get());
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_pResource = resource;
|
||||
popup->m_pWindowOwner = pOwner->m_pWindowOwner;
|
||||
popup->m_pLayerOwner = pOwner->m_pLayerOwner;
|
||||
popup->m_pParent = pOwner;
|
||||
popup->m_pSelf = popup;
|
||||
popup->m_pWLSurface = CWLSurface::create();
|
||||
popup->m_pWLSurface->assign(resource->surface->surface.lock(), popup.get());
|
||||
|
||||
popup->m_lastSize = resource->m_surface->m_current.geometry.size();
|
||||
popup->m_vLastSize = resource->surface->current.geometry.size();
|
||||
popup->reposition();
|
||||
|
||||
popup->initAllSignals();
|
||||
@@ -45,112 +46,104 @@ UP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
||||
}
|
||||
|
||||
CPopup::~CPopup() {
|
||||
if (m_wlSurface)
|
||||
m_wlSurface->unassign();
|
||||
if (m_pWLSurface)
|
||||
m_pWLSurface->unassign();
|
||||
}
|
||||
|
||||
void CPopup::initAllSignals() {
|
||||
|
||||
if (!m_resource) {
|
||||
if (!m_windowOwner.expired())
|
||||
m_listeners.newPopup = m_windowOwner->m_xdgSurface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
|
||||
else if (!m_layerOwner.expired())
|
||||
m_listeners.newPopup = m_layerOwner->m_layerSurface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
|
||||
if (!m_pResource) {
|
||||
if (!m_pWindowOwner.expired())
|
||||
listeners.newPopup = m_pWindowOwner->m_pXDGSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
else if (!m_pLayerOwner.expired())
|
||||
listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
else
|
||||
ASSERT(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_listeners.reposition = m_resource->m_events.reposition.listen([this] { this->onReposition(); });
|
||||
m_listeners.map = m_resource->m_surface->m_events.map.listen([this] { this->onMap(); });
|
||||
m_listeners.unmap = m_resource->m_surface->m_events.unmap.listen([this] { this->onUnmap(); });
|
||||
m_listeners.dismissed = m_resource->m_events.dismissed.listen([this] { this->onUnmap(); });
|
||||
m_listeners.destroy = m_resource->m_surface->m_events.destroy.listen([this] { this->onDestroy(); });
|
||||
m_listeners.commit = m_resource->m_surface->m_events.commit.listen([this] { this->onCommit(); });
|
||||
m_listeners.newPopup = m_resource->m_surface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
|
||||
listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); });
|
||||
listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); });
|
||||
listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
listeners.dismissed = m_pResource->events.dismissed.registerListener([this](std::any d) { this->onUnmap(); });
|
||||
listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); });
|
||||
listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); });
|
||||
listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast<SP<CXDGPopupResource>>(d)); });
|
||||
}
|
||||
|
||||
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
|
||||
const auto& POPUP = m_children.emplace_back(CPopup::create(popup, m_self));
|
||||
POPUP->m_self = POPUP;
|
||||
const auto& POPUP = m_vChildren.emplace_back(CPopup::create(popup, m_pSelf));
|
||||
POPUP->m_pSelf = POPUP;
|
||||
Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
|
||||
}
|
||||
|
||||
void CPopup::onDestroy() {
|
||||
m_inert = true;
|
||||
m_bInert = true;
|
||||
|
||||
if (!m_parent)
|
||||
if (!m_pParent)
|
||||
return; // head node
|
||||
|
||||
std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; });
|
||||
std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; });
|
||||
}
|
||||
|
||||
void CPopup::onMap() {
|
||||
if (m_mapped)
|
||||
if (m_bMapped)
|
||||
return;
|
||||
|
||||
m_mapped = true;
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
m_bMapped = true;
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS);
|
||||
|
||||
CBox box = m_wlSurface->resource()->extends();
|
||||
CBox box = m_pWLSurface->resource()->extends();
|
||||
box.translate(COORDS).expand(4);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
m_lastPos = coordsRelativeToParent();
|
||||
m_vLastPos = coordsRelativeToParent();
|
||||
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
m_subsurfaceHead = CSubsurface::create(m_self);
|
||||
m_pSubsurfaceHead = CSubsurface::create(m_pSelf);
|
||||
|
||||
//unconstrain();
|
||||
sendScale();
|
||||
m_resource->m_surface->m_surface->enter(PMONITOR->m_self.lock());
|
||||
m_pResource->surface->surface->enter(PMONITOR->self.lock());
|
||||
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
}
|
||||
|
||||
void CPopup::onUnmap() {
|
||||
if (!m_mapped)
|
||||
if (!m_bMapped)
|
||||
return;
|
||||
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
if (!m_pResource || !m_pResource->surface) {
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
m_mapped = false;
|
||||
m_bMapped = false;
|
||||
|
||||
// if the popup committed a different size right now, we also need to damage the old size.
|
||||
const Vector2D MAX_DAMAGE_SIZE = {std::max(m_lastSize.x, m_resource->m_surface->m_surface->m_current.size.x),
|
||||
std::max(m_lastSize.y, m_resource->m_surface->m_surface->m_current.size.y)};
|
||||
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
|
||||
CBox box = m_wlSurface->resource()->extends();
|
||||
CBox box = m_pWLSurface->resource()->extends();
|
||||
box.translate(COORDS).expand(4);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
// damage the last popup's explicit max size as well
|
||||
box = CBox{COORDS, MAX_DAMAGE_SIZE}.expand(4);
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
m_pSubsurfaceHead.reset();
|
||||
|
||||
m_subsurfaceHead.reset();
|
||||
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
|
||||
// damage all children
|
||||
breadthfirst(
|
||||
[](WP<CPopup> p, void* data) {
|
||||
if (!p->m_resource)
|
||||
if (!p->m_pResource)
|
||||
return;
|
||||
|
||||
auto box = CBox{p->coordsGlobal(), p->size()};
|
||||
@@ -166,59 +159,59 @@ void CPopup::onUnmap() {
|
||||
}
|
||||
|
||||
void CPopup::onCommit(bool ignoreSiblings) {
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
if (!m_pResource || !m_pResource->surface) {
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_resource->m_surface->m_initialCommit) {
|
||||
m_resource->m_surface->scheduleConfigure();
|
||||
if (m_pResource->surface->initialCommit) {
|
||||
m_pResource->surface->scheduleConfigure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_windowOwner.expired() && (!m_windowOwner->m_isMapped || !m_windowOwner->m_workspace->m_visible)) {
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) {
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
|
||||
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
|
||||
if (*PLOGDAMAGE)
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock());
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowOwner.lock());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_resource->m_surface->m_mapped)
|
||||
if (!m_pResource->surface->mapped)
|
||||
return;
|
||||
|
||||
const auto COORDS = coordsGlobal();
|
||||
const auto COORDSLOCAL = coordsRelativeToParent();
|
||||
|
||||
if (m_lastSize != m_resource->m_surface->m_surface->m_current.size || m_requestedReposition || m_lastPos != COORDSLOCAL) {
|
||||
CBox box = {localToGlobal(m_lastPos), m_lastSize};
|
||||
if (m_vLastSize != m_pResource->surface->surface->current.size || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) {
|
||||
CBox box = {localToGlobal(m_vLastPos), m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
m_lastSize = m_resource->m_surface->m_surface->m_current.size;
|
||||
box = {COORDS, m_lastSize};
|
||||
m_vLastSize = m_pResource->surface->surface->current.size;
|
||||
box = {COORDS, m_vLastSize};
|
||||
g_pHyprRenderer->damageBox(box);
|
||||
|
||||
m_lastPos = COORDSLOCAL;
|
||||
m_vLastPos = COORDSLOCAL;
|
||||
}
|
||||
|
||||
if (!ignoreSiblings && m_subsurfaceHead)
|
||||
m_subsurfaceHead->recheckDamageForSubsurfaces();
|
||||
if (!ignoreSiblings && m_pSubsurfaceHead)
|
||||
m_pSubsurfaceHead->recheckDamageForSubsurfaces();
|
||||
|
||||
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y);
|
||||
g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y);
|
||||
|
||||
m_requestedReposition = false;
|
||||
m_bRequestedReposition = false;
|
||||
|
||||
if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer));
|
||||
if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer));
|
||||
}
|
||||
|
||||
void CPopup::onReposition() {
|
||||
Debug::log(LOG, "Popup {:x} requests reposition", (uintptr_t)this);
|
||||
|
||||
m_requestedReposition = true;
|
||||
m_bRequestedReposition = true;
|
||||
|
||||
m_lastPos = coordsRelativeToParent();
|
||||
m_vLastPos = coordsRelativeToParent();
|
||||
|
||||
reposition();
|
||||
}
|
||||
@@ -230,32 +223,32 @@ void CPopup::reposition() {
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
CBox box = {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
|
||||
m_resource->applyPositioning(box, COORDS);
|
||||
CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y};
|
||||
m_pResource->applyPositioning(box, COORDS);
|
||||
}
|
||||
|
||||
SP<CWLSurface> CPopup::getT1Owner() {
|
||||
if (m_windowOwner)
|
||||
return m_windowOwner->m_wlSurface;
|
||||
if (m_pWindowOwner)
|
||||
return m_pWindowOwner->m_pWLSurface;
|
||||
else
|
||||
return m_layerOwner->m_surface;
|
||||
return m_pLayerOwner->surface;
|
||||
}
|
||||
|
||||
Vector2D CPopup::coordsRelativeToParent() {
|
||||
Vector2D offset;
|
||||
|
||||
if (!m_resource)
|
||||
if (!m_pResource)
|
||||
return {};
|
||||
|
||||
WP<CPopup> current = m_self;
|
||||
offset -= current->m_resource->m_surface->m_current.geometry.pos();
|
||||
WP<CPopup> current = m_pSelf;
|
||||
offset -= current->m_pResource->surface->current.geometry.pos();
|
||||
|
||||
while (current->m_parent && current->m_resource) {
|
||||
while (current->m_pParent && current->m_pResource) {
|
||||
|
||||
offset += current->m_wlSurface->resource()->m_current.offset;
|
||||
offset += current->m_resource->m_geometry.pos();
|
||||
offset += current->m_pWLSurface->resource()->current.offset;
|
||||
offset += current->m_pResource->geometry.pos();
|
||||
|
||||
current = current->m_parent;
|
||||
current = current->m_pParent;
|
||||
}
|
||||
|
||||
return offset;
|
||||
@@ -270,30 +263,30 @@ Vector2D CPopup::localToGlobal(const Vector2D& rel) {
|
||||
}
|
||||
|
||||
Vector2D CPopup::t1ParentCoords() {
|
||||
if (!m_windowOwner.expired())
|
||||
return m_windowOwner->m_realPosition->value();
|
||||
if (!m_layerOwner.expired())
|
||||
return m_layerOwner->m_realPosition->value();
|
||||
if (!m_pWindowOwner.expired())
|
||||
return m_pWindowOwner->m_vRealPosition->value();
|
||||
if (!m_pLayerOwner.expired())
|
||||
return m_pLayerOwner->realPosition->value();
|
||||
|
||||
ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
void CPopup::recheckTree() {
|
||||
WP<CPopup> curr = m_self;
|
||||
while (curr->m_parent) {
|
||||
curr = curr->m_parent;
|
||||
WP<CPopup> curr = m_pSelf;
|
||||
while (curr->m_pParent) {
|
||||
curr = curr->m_pParent;
|
||||
}
|
||||
|
||||
curr->recheckChildrenRecursive();
|
||||
}
|
||||
|
||||
void CPopup::recheckChildrenRecursive() {
|
||||
if (m_inert || !m_wlSurface)
|
||||
if (m_bInert || !m_pWLSurface)
|
||||
return;
|
||||
|
||||
std::vector<WP<CPopup>> cpy;
|
||||
std::ranges::for_each(m_children, [&cpy](const auto& el) { cpy.emplace_back(el); });
|
||||
std::ranges::for_each(m_vChildren, [&cpy](const auto& el) { cpy.emplace_back(el); });
|
||||
for (auto const& c : cpy) {
|
||||
c->onCommit(true);
|
||||
c->recheckChildrenRecursive();
|
||||
@@ -301,25 +294,25 @@ void CPopup::recheckChildrenRecursive() {
|
||||
}
|
||||
|
||||
Vector2D CPopup::size() {
|
||||
return m_lastSize;
|
||||
return m_vLastSize;
|
||||
}
|
||||
|
||||
void CPopup::sendScale() {
|
||||
if (!m_windowOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->m_wlSurface->m_lastScaleFloat);
|
||||
else if (!m_layerOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->m_surface->m_lastScaleFloat);
|
||||
if (!m_pWindowOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pWindowOwner->m_pWLSurface->m_fLastScale);
|
||||
else if (!m_pLayerOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pLayerOwner->surface->m_fLastScale);
|
||||
else
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool CPopup::visible() {
|
||||
if (!m_windowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
|
||||
if (!m_layerOwner.expired())
|
||||
if (!m_pWindowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_pWindowOwner.lock());
|
||||
if (!m_pLayerOwner.expired())
|
||||
return true;
|
||||
if (m_parent)
|
||||
return m_parent->visible();
|
||||
if (m_pParent)
|
||||
return m_pParent->visible();
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -333,8 +326,8 @@ void CPopup::bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(W
|
||||
nodes2.reserve(nodes.size() * 2);
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
for (auto const& c : n->m_children) {
|
||||
nodes2.push_back(c->m_self);
|
||||
for (auto const& c : n->m_vChildren) {
|
||||
nodes2.push_back(c->m_pSelf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +337,7 @@ void CPopup::bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(W
|
||||
|
||||
void CPopup::breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data) {
|
||||
std::vector<WP<CPopup>> popups;
|
||||
popups.push_back(m_self);
|
||||
popups.push_back(m_pSelf);
|
||||
bfHelper(popups, fn, data);
|
||||
}
|
||||
|
||||
@@ -353,14 +346,14 @@ WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
breadthfirst([&popups](WP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
|
||||
|
||||
for (auto const& p : popups | std::views::reverse) {
|
||||
if (!p->m_resource || !p->m_mapped)
|
||||
if (!p->m_pResource || !p->m_bMapped)
|
||||
continue;
|
||||
|
||||
if (!allowsInput) {
|
||||
const bool HASSURFACE = p->m_resource && p->m_resource->m_surface;
|
||||
const bool HASSURFACE = p->m_pResource && p->m_pResource->surface;
|
||||
|
||||
Vector2D offset = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.pos() : Vector2D{};
|
||||
Vector2D size = HASSURFACE ? p->m_resource->m_surface->m_current.geometry.size() : p->size();
|
||||
Vector2D offset = HASSURFACE ? p->m_pResource->surface->current.geometry.pos() : Vector2D{};
|
||||
Vector2D size = HASSURFACE ? p->m_pResource->surface->current.geometry.size() : p->size();
|
||||
|
||||
if (size == Vector2D{})
|
||||
size = p->size();
|
||||
@@ -369,7 +362,7 @@ WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
if (BOX.containsPoint(globalCoords))
|
||||
return p;
|
||||
} else {
|
||||
const auto REGION = CRegion{p->m_wlSurface->resource()->m_current.input}.intersect(CBox{{}, p->m_wlSurface->resource()->m_current.size}).translate(p->coordsGlobal());
|
||||
const auto REGION = CRegion{p->m_pWLSurface->resource()->current.input}.intersect(CBox{{}, p->m_pWLSurface->resource()->current.size}).translate(p->coordsGlobal());
|
||||
if (REGION.containsPoint(globalCoords))
|
||||
return p;
|
||||
}
|
||||
@@ -379,5 +372,5 @@ WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
}
|
||||
|
||||
bool CPopup::inert() const {
|
||||
return m_inert;
|
||||
return m_bInert;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user